next up previous contents
Next: El cálculo Lambda Up: Lenguajes funcionales Previous: La estructura de los   Contents


La importancia de los lenguajes funcionales

Se considerará un caso en que las ideas dadas al final de la sección anterior pueden aplicarse con resultados apreciables. El caso se desarrolla en [Hug84]:

Se trata del problema de realizar la suma de todos los elementos de una lista. También las listas, como las fórmulas y los lenguajes, pueden definirse inductivamente:

Intuitivamente, si l es la lista:

  < x1, x2, ... , xN >

Cons ( x0, l ) es la lista

  < x0, x1, x2, ... , xN >

De este modo, las listas no vacías pueden pensarse como compuestas de un elemento (su cabeza) y otra lista (su cola).

Se supondrá que pueden aplicarse sobre listas las funciones:

Ahora puede definirse la función en cuestión, siguiendo un esquema recursivo:

  List_suma :- (l) if Vacía? (l)
                      then 0
                      else Cabeza (l) + List_suma (Resto (l) )

es decir, se define la imagen de la lista primitiva (Vacía) y luego se enuncia la regla para calcular la imagen de una lista compuesta en función de su cabeza y de la imagen de su cola.

El esquema sería idéntico si se quisiera definir el producto de todos los objetos de la lista, en lugar de la suma. Las modificaciones a introducir se marcan en la siguiente figura:

                                     1
  List_suma :- (l) if Vacía? (l)    /
                      then [0] <---+
                      else Cabeza (l) [+] List_suma (Resto (l) )
                                       ^
                                       |
                                       *

Esto muestra un patrón recursivo, general para el tratamiento de listas, que está siendo aplicado a las partes entre corchetes.Con este enfoque se está sugiriendo una partición del problema que produciría un módulo de aplicación general, representando el patrón citado. Este módulo puede definirse como una función:

  reduce :- (f) (b) (l) if Vacía? (l)
                           then b
                           else
                                f (Cabeza (l))
                                  (reduce (f) (b) (Resto (l)))

donde se ha hecho abstracción, a partir de la expresión de List_suma, de:

En términos de la jerg clásica podría decirse que se ha construído un módulo que representa una forma general de tratamiento de listas que, combinada con funciones adecuadas, construye una variedad de programas concretos. Efectivamente, los anteriores programas de suma y producto de elementos de una lista son ahora resultados de aplicaciones convenientes de este módulo más general:

  List_suma :- reduce (+) (0)
  List_product :- reduce (*) (1)

que, como puede verse, a partir de la definición de reduce, son, como antes, funciones, cada una de un argumento, que se espera represente una lista.

De igual modo podrán construirse módulos similares para tratar otras clases de objetos, lo que equivale a decir que pueden definirse constructores de programas adaptados a cada aplicación particular.

Para esto es básico el concepto de orden superior, que autoriza a realizar abstracciones sobre funciones, es decir a que éstas sean representadas por variables que son argumentos de otras funciones.

Esta propiedad es un atributo esencial de los lenguajes funcionales.

En el caso general de los lenguajes imperativos hay, por ejemplo, una sustancial diferencia conceptual entre los programas y sus argumentos. Estos últimos (los llamados datos) denotan direcciones de una memoria con valores asociados mientras que los programas representan una transformación de, precisamente, tal asociación entre direcciones y valores. En los lenguajes en que esta heterogeneidad de significado entre los programas y sus argumentos se mantiene estrictamente, el concepto de orden superior no puede ser incorporado: una variable que sea argumento de un programa sólo puede representar una dirección de memoria que aquel eventualmente habrá de afectar y, por lo tanto, no otro programa.

Como consecuencia, las formas en que los programas pueden combinarse en estos lenguajes quedan restringidas a la aplicación de un conjunto preestablecido de constructores (las estructuras de control) sobre programas fijos (ya sea referenciados por un nombre o por una estructura expresa), lo cual limita la posibilidad de desarrollar módulos de aplicación general. Las formas generales de tratamiento de objetos como reduce pueden tratarse, en todo caso, como esquemas de programas, pero tal esquema no es un elemento del lenguaje y cada programa debe reproducir, en su construcción, la estructura del mismo.

Esta concepción, debida a la semántica particular de programas y datos, es tomada del modelo de cálculo en que estos lenguajes se fundamentan que corresponde al cálculo por efecto de los computadores Von Neumann.

En el esquema funcional, por el contrario, todas las expresiones denotan un valor, representando tanto el concepto de dato como el de función o programa. La diferencia es apenas una cuestión de interpretación:

Según esta interpretación, no hay más diferencias entre un entero y la función + (de dos argumentos) que entre ésta y, por ejemplo, List_suma (que admite un solo argumento).

Todos son casos particulares del concepto sintáctico de expresión, interpretado como valor y los constructores de expresiones (aplicación y abstracción funcional) deberían interpretarse simplemente como operadores cuyo resultado es una expresión más evaluada (en el caso de la aplicación) o menos evaluada (en el de la abstracción funcional) que sus operandos.

Otras consecuencias interesantes de esta uniformidad conceptual entre datos y programas pueden ilustrarse con otro ejemplo.

Considérese el problema de definir funciones para representar las operaciones clásicas sobre conjuntos: unión, intersección, pertenencia de un elemento a un conjunto.

Se supondrá, además, que los conjuntos a manejar son definidos por comprensión, es decir, por una propiedad que deben cumplir sus elementos, los cuales supondremos, para fijar ideas, que son, en todos los casos, números naturales.

Con esta idea, tales conjuntos pueden representarse por medio de funciones que, aplicadas a un elemento, devuelven Verdadero si éste tiene la propiedad que define al conjunto, y Falso en caso contrario.

Por ejemplo, la función

  Pares :- (x) (x / 2) = 0

define el conjunto de los pares, si se asume que = representa el predicado de igualdad en los naturales y, análogamente puede interpretarse:

  Menores_que_10 :- (x) x < 10

De este modo, los datos del problema (conjuntos) están siendo representados por funciones que pueden ser calculadas aplicándolas a argumentos (los que se llamarían programas en la concepción tradicional) y no como estructuras localizadas en una memoria.

Ahora las operaciones clásicas sobre estos conjuntos pueden recibir representaciones muy sencillas:

TODAVÍA NO HEMOS COMPUESTO ESTA PARTE DEL LIBRO, POR FAVOR TENGA PACIENCIA. MUCHAS GRACIAS.




next up previous contents
Next: El cálculo Lambda Up: Lenguajes funcionales Previous: La estructura de los   Contents
Cesar Ballardini
2003-10-14
Hosted by www.Geocities.ws

1