-
LOGRO: Identifica características de Ocaml
- LOGRO: Maneja conceptos de programación funcional pura
- LOGRO: Maneja conceptos de tipos
1.1 Vistazo de programación funcional y Ocaml
Indicadores de logro:
-
INDICADOR: Puede reducir expresiones que incluyan funciones y aplicación funcional
- INDICADOR: Asocia expresiones a nombres empleando let
- INDICADOR: Define funciones sencillas que emplean tipos básicos
- INDICADOR: Deriva tipo de expresiones sencillas que incluyan tipos básicos
- INDICADOR: Emplea la documentación de Ocaml
- INDICADOR: Emplea el entorno interactivo de Ocaml
Vistazo a Ocaml
Ocaml es uno entre muchos lenguajes funcionales (como Lisp, Scheme, Haskell)
que además de programación funcional pura, permite programar empleando
características imperativas, algunas características propias
de lenguajes orientados a objetos e incluye un sistema de módulos que
facilita el desarrollo de proyectos grandes (con signaturas y functores).
Se ha desarrollado desde 1992, con base en su predecesor Caml Special Light
(inicio de la década 1990), cuyo predecesor a su vez es Caml Light (1985-1990)
que pertenece a la familia de lenguajes de programación funcionales ML
(que se inicio en la década de 1970).
Como los demás lenguajes funcionales emplea un recolector de basura que
permite en muchos casos desentenderse del manejo de memoria dinámica
(el recolector elimina automáticamente memoria que no se usa --de forma
que no es necesario usar free como en C).
Cuenta con un sistema de tipos fuerte, con un derivador de tipos que
facilita el chequeo estático de programas sin requerir del programador
tiempo en declaración explicita de tipos. Además de tipos básicos (enteros,
flotantes, booleanos, caracteres y cadenas) ofrece diversas formas de construir
nuevos tipos como tuplas, arreglos, listas, registros, conjuntos, streams y
facilidades para definir tipos recursivos, polimórficos y paramétricos.
En cuanto a interpretación/compilación se basa en un modelo de máquina
abstracta portable (máquina Zinc). Cuenta con un interprete (ocaml)
que es a su vez un entorno interactivo útil para experimentar y dos
compiladores:
-
ocamlc que genera bytecode utilizable por la máquina Zinc y portable
a toda arquitectura donde se ha portado la máquina Zinc (incluyendo
x386/OpenBSD, x386/Linux, x386/Windows 95-NT, Mac, Digital, Solaris, IRIX)
- ocamlopt que genera binarios para diversas arquitecturas
(incluyendo procesadores i386 con sistemas operativos OpenBSD y Linux).
Incluso hay un compilador sobre la marcha (JIT o Just in Time Compiler)
que traduce en línea bytecode a código de máquina [4].
Cuenta con herramientas que facilitan el desarrollo como un depurador
simbólico; herramientas para análisis léxico y sintáctico; herramientas
para automatizar compilación de proyectos grandes; editor y visualizador
gráfico de módulos; modos para diversos editores; preprocesador muy
configurable pues permite redefinir la sintaxis del lenguaje; extractor de
documentación técnica; medidor de desempeño.
La distribución básica incluye diversas librerías portables para
manejo de estructuras de datos (e.g. tablas de hashing, conjuntos, pilas),
gráficas, aritmética de precisión arbitraria, multi-threading,
emulación de Unix, interfaz para Tcl/Tk y C. Hay también disponibles
librerías desarrolladas por diversas personas y grupos (ver índices
en:
http://caml.inria.fr/humps/caml_latest.html y
http://www.npc.de/ocaml/linkdb/)
Ciertos tipos de problemas pueden programarse de forma MUY eficiente
empleando Ocaml como lo muestran los resultados en las competencias
internacionales ICFP realizadas desde 1998, en los que han participado
grupos de programadores empleando su lenguaje de programación favorito
(incluyendo lenguajes imperativos como C, orientados a objetos como
Java y funcionales como Ocaml). De las 5 versiones de 1998 a 2002,
equipos que han programado en Ocaml han ocupado el primer puesto en
3 oportunidades, el segundo puesto en 2 y el tercero en uno.
En cuanto a licencia, las herramientas de Ocaml emplean la GPL, mientras
que las librerías la LGPL la cual permite poner la licencia que
se desee a programas producidos con Ocaml y enlazados con las librerías.
Programación funcional pura
En programación funcional pura no hay estados, ni asignación, ni
secuenciación, sólo funciones y aplicación funcional. En Ocaml
podría programarse de forma funcional pura usando funciones
anónimas, un ejemplo de una función anónima es:
fun x -> x+1;;
que define la función sucesor. Note que esta función
asocia una variable x con una expresión x+11.
Esta función podría aplicarse al número 2, por ejemplo:
(fun x -> x+1) 2;;
Operacionalmente aplicar un valor a una función significa remplazar
en la expresión la variable de la función por el valor2. En este ejemplo la aplicación resultaría en
2+1
Esta expresión es a su vez una aplicación funcional que al
reducirse3 da 3.
En programación funcional, una función es un ente de primera clase como
las constantes y variables. Es decir que puede emplearse en expresiones
y usarse como se usan constantes y variables. Por ejemplo :
fun x-> (fun y -> (x+y)/2)
es una función cuya expresión incluye otra función. Note que al aplicar
esta función a un valor se obtendría otra función:
(fun x-> (fun y -> (x+y)/2)) 5
®b
fun y -> (5+y)/2
Que promediaría un valor (y) con la constante 5.
Como abreviación para fun x -> fun y -> exp
puede emplearse:
fun x y -> exp4.
let asocia nombres con expresiones
Para asociar un nombre con una expresión se emplea
let nombre = expresion;;
por ejemplo:
let prom=(fun x -> (fun y -> (x+y)/2));;
asociará el nombre prom con una función que recibe dos enteros
y calcula el promedio entre ellos.
Puede usarse por ejemplo así:
prom 2 6
que reducirá a 4. O por ejemplo
let prom10=prom 10;;
que asociará el nombre prom10 a una función que recibe un entero
y calcula el promedio entre 10 y ese entero.
La palabra reservada let también permite abreviar la
definición de funciones:
let f=fun x->exp
puede escribirse como
let f x=exp
Así que la función prom pudo haberse escrito como
let prom x y= (x+y)/2;;
Derivación de tipos
Los tipos básicos en Ocaml son enteros int (e.g 2, -100),
flotantes (e.g -0.23) float ,
caracteres char (e.g 'a') y cadenas string
(e.g "Dios nos ama"). Para
cada uno de estos tipos hay diversas funciones definidas.
Ocaml es fuertemente tipado es decir que toda expresión tiene un tipo
único y sólo pueden aplicarse valores a funciones cuando el tipo
del valor corresponde al tipo esperado por la función.
Por ejemplo (+) es la función que suma dos enteros, mientras (+.) es la que
que suma dos flotantes (en general los operadores entre enteros tienen
una análogo para flotantes cuyo nombre termina con el carácter punto).
La ventaja de este tipado fuerte es la posibilidad de derivar automáticamente
el tipo de expresiones (librando al programador de esa tarea). Por ejemplo
una expresión como
fun y -> y +. y
es una función que recibe un flotante y retorna un flotante, lo derivamos
sabiendo que (+.) recibe dos flotantes y retorna un flotante.
1.1.2 Hacía la práctica
Sobre documentación
Hay abundante documentación sobre Ocaml, aunque la mayoría esta
en inglés, la referencia más exacta aunque tal vez no la más
fácil es la guía del usuario (ver [3]).
Para aprender además de estas guías sugerimos Developing
Applications in Ocaml (ver [1]).
Sobre instalación
Hay binarios precompilados de la versión más reciente de
Ocaml para diversas plataformas (incluyendo x86/Windows) en el
sitio de distribución http://caml.inria.fr/ocaml. Para seguir
estas guías sugerimos de forma especial emplearlo en un ambiente tipo
Unix (puede ser un sistema operativo de fuentes abiertas como
OpenBSD o Linux). Para algunas plataformas tipo Unix
hay precompilados disponibles, en otras tipo Unix pueden descargarse
las fuentes y compilarse (proceso que generalmente no tiene
complicaciones). Junto con las fuentes más recientes se
distribuye un documento (INSTALL) que explica como realizar la compilación
y que hacer en caso de algunas dificultades.
En caso de compilar, sugerimos que primero instale Tcl/Tk y
se asegure de compilar con soporte para LablTk, de forma que
pueda crear interfaces gráficas desde Ocaml usando Tcl/Tk.
Uso del entorno interactivo
Una vez instalado Ocaml puede emplearse el entorno interactivo para
experimentar y hacer los primeros programas. Se inicia con:
ocaml
Allí pueden ingresarse construcciones válidas del lenguaje terminadas
con ;; para que sean reducidas.
Por ejemplo al ingresar la constante 2
# 2;;
- : int = 2
# es el prompt de este entorno interactivo 2;; es el dato
ingresado y la respuesta del entorno interactivo es el tipo de
la expresión reducida.
Por ejemplo al ingresar una función que eleva un entero al cuadrado:
# fun x -> x*x;;
- : int -> int = < fun >
el entorno interactivo analiza tipos y contesta indicando que
la expresión es una función que recibe un entero y retorna un
entero.
Al ingresar una aplicación funcional el entorno realiza la reducción
completa y retorna el tipo (y de ser posible el valor) de la
expresión resultante:
# (fun x -> x*x) 5;;
- : int = 25
Note que las funciones que se definan tienen asociatividad derecha, es
decir:
fun x -> fun y -> x+y;;
equivale a
fun x -> (fun y -> x+y);;
el tipo que Ocaml responde para esta función es:
int -> int -> int = <fun>
que puede leerse como
int -> (int -> int) = <fun>
Es decir una función que recibe un entero y devuelve una función de
enteros en enteros.
A diferencia por ejemplo de:
fun f -> 1+(f 5);;
cuyo tipo es:
(int -> int) -> int
es decir es una función que retorna un entero y que recibe una función
de enteros en enteros.
Además de poder ingresar expresiones directamente en el entorno interactivo
pueden cargarse de un archivo fuente empleando:
#use "archivo";;
O desde la línea de comandos pueden interpretarse las expresiones de un
archivo ejecutando:
ocaml archivo
1.1.3 Lecturas recomendadas
Un vistazo más completo de Ocaml puede consultarse en:
http://caml.inria.fr/ercim.html y en
http://caml.inria.fr/FAQ/general-eng.html
Puede consultar más sobre programación funcional pura y sus bases
teóricas (calculo l) en los 3 primeros capítulos
de [2].
Para lograr la instalación además de la documentación oficial de Ocaml
puede consultar el capítulo correspondiente de [1].
1.1.4 Ejercicios para afianzar teoría
-
¿Qué expresión resulta tras aplicar el valor 3 a la siguiente función
(haga sólo un paso de reducción)?:
fun z -> z+z;;
-
¿Qué valor resulta tras hacer la reducción completa de la siguiente expresión?:
(fun var1 -> (fun var2 -> (var1*var1)+var2)) 2 3;;
-
La función suma puede verse en una notación prefija así: (+) 2 3 que
equivale a 2+3. ¿Qué valor se obtiene al reducir por completo la
siguiente expresión?:
(fun v -> (+) v 3) 5 ;;
-
¿Qué valor resulta tras reducir por completo la siguiente aplicación?:
(fun f -> 1+(f 5)) (fun y -> 2*y);;
-
¿Qué valor resulta tras reducir por completo la siguiente aplicación?:
(fun f x y -> (f x)+(f y)) (fun y -> 2*y) 3 4;;
-
Consulte en la documentación de Ocaml la lista de operadores. ¿En que sección
del manual está dicha lista?
-
¿Cual es el tipo de la siguiente expresión?
fun s t -> s t
-
Busque en la documentación del módulo Pervasives una función que
permita convertir un entero en flotante. Después defina una función
que reciba una variable entera x y retorne el flotante que resulta
de sumar x (como flotante) con 0.5.
-
Suponga que ha ingresado en el entorno interactivo la función dif:
let dif=fun f x h-> ((f (x+.h))-.(f x))/.h;;
que calcula la aproximación a la pendiente de la tangente de una función
en un punto x (usando como referencia el punto x+.h.
Emplee esta función para definir una nueva función con nombre
dif02 que haga lo mismo que la anterior pero fijando el valor de
h en 0.2.
-
Use la función dif02 del ejercicio anterior para calcular una
aproximación a la derivada de la función x2 en x=1.
¿Cuál es el error en la
aproximación? (el error entre un par de valores u y v es
u - v ).
1.1.5 Ejercicios de programación
-
En un archivo de nombre areas.ml asocie el nombre areatriangulo
a una función cuyo tipo sea float -> float -> float
y que calcule el área de un triángulo, dada la base (primer parámetro)
y la altura (segundo parámetros).
Asocie el nombre alturatriangulo a una función de tipo
float -> float -> float -> float que calcule la altura de
un triángulo, recibiendo como primer parámetro la base (lado mayor),
y como segundo y tercer parámetros la longitud de los otros dos lados.
Puede serle de utilidad emplear la función sqrt: float -> float que
retorna la raíz de un flotante no negativo.
Finalmente asocie el nombre areatriangulo2 a una función de tipo
float -> float -> float -> float que calcule el área de un
triángulo recibiendo los mismos parámetros de la función
alturatriangulo (sugerimos que emplee la
función alturatriangulo).
- 1
- En este ejemplo
decimos que la variable x está acotada en la expresión.
- 2
- Esta
aplicación en cálculo lambda (l), se llama reducción
beta (b).
- 3
- Reducir significa efectuar aplicaciones
funcionales reiteradamente hasta obtener una expresión que
no puede aplicarse más.
- 4
- fun permite definir una función,
function también aunque este último no permite la abreviación
mencionada.