Previous Up Next

Capítulo 1  Introducción

1.1  Vistazo de programación funcional y Ocaml

Indicadores de logro:

1.1.1  Teoría

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: 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

  1. ¿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;;
  2. ¿Qué valor resulta tras hacer la reducción completa de la siguiente expresión?: (fun var1 -> (fun var2 -> (var1*var1)+var2)) 2 3;;
  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 ;;
  4. ¿Qué valor resulta tras reducir por completo la siguiente aplicación?: (fun f -> 1+(f 5)) (fun y -> 2*y);;
  5. ¿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;;
  6. Consulte en la documentación de Ocaml la lista de operadores. ¿En que sección del manual está dicha lista?
  7. ¿Cual es el tipo de la siguiente expresión? fun s t -> s t
  8. 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.
  9. 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.

  10. 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

  1. 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.

Previous Up Next