PROGRAMACION FUNCIONAL - Un lenguaje de programación funcional tiene gran flexibilidad, es conciso en su notación y su semántica es sencilla. El inconveniente principal de estos lenguajes ha sido la ineficiencia en la ejecución de los mismos. La programación funcional tiene poderosos mecanismos para controlar la complejidad y para estructurar el código, pero son más abstractos y matemáticos y por lo tanto no pueden manejarse tan fácilmente. 1.- Programas como funciones Un programa es una descripción de un cálculo específico. Un programa es equivalente a una función matemática y= f(x) o f:x -> Y. El conjunto X se llama el dominio de f, en tanto que el conjunto Y se conoce como el rango de f.
PROGRAMACION FUNCIONAL Podemos pensar en programas, procedimientos y funciones en un lenguaje de programación o representados por el concepto matemático de una función. En el caso de un programa: x representa la entrada e y la salida. En el caso de un procedimiento o de una función, x representa los parámetros e y los valores devueltos. Podemos decir que x representa las entradas a un programa e y las salidas, además no se hace distinción entre programa, procedimientos y funciones.
PROGRAMACION FUNCIONAL En los lenguajes de programación debemos distinguir entre: -Definición de función: la primera es una declaración que describe la forma en la que debe calcularse una función utilizando parámetros. -Aplicación de función: es una llamada a una función declarada utilizando parámetros reales, es decir, argumentos. La programación funcional pura es completa en Turing porque cualquier cálculo puede ser descripto utilizando sólo funciones. En la programación funcional se elimina la asignación como una operación disponible.
Una consecuencia de la falta de asignaciones en la programación es que tampoco pueden existir ciclos. En efecto un lazo debe tener una variable de control que es reasignada conforme se ejecuta el lazo o ciclo, y esto no es posible sin la asignación. Entonces cómo escribimos operaciones que se repitan en forma secuencial? La característica esencial es la recursión. void gcd (int u, int v, int* x) int gcd ( int u, int v) { int y, t, z ; { if (v==0)return u; Z=u ; y=v; else return gcd (v, u / v); While (y!=0) } t=y; Y= z / y; Z= t; }*x=z; Versión imperativa utilizando un lazo o ciclo Versión funcional con recursión
- La propiedad de una función de que su valor dependa sólo de los valores de sus argumentos ( y de sus constantes no locales) se conoce como transparencia referencial. Por ejemplo la función gcd es transparente referencialmente puesto que su valor depende sólo del valor de sus argumentos. Una función transparente referencialmente sin parámetros deberá siempre devolver el mismo valor y por lo tanto no tiene ninguna diferencia con una constante. - La carencia de asignación y la transparencia referencial de la programación funcional hacen que la semántica de los programas funcionales resulte particularmente simple: no existe una noción explicita de estado, ya que no hay concepto de localizaciones en la memoria con valores cambiantes(una localización de memoria implicaría la existencia de una variable). - El ambiente de ejecución asocia nombres sólo a valores ( y no a localizaciones de memoria) y una vez introducido un nombre en el ambiente, su valor no puede cambiar jamás. Esta idea de semántica se conoce como semántica de valor, a fin de distinguirla de la semántica de almacenamiento más usual.
- Las funciones deben considerarse como valores que pueden ser calculados por otras funciones y que también pueden ser parámetros de funciones. Expresamos esta generalidad de las funciones en la programación funcional diciendo que las funciones son valores de primera clase. - Por ej: una de las operaciones esenciales de las funciones es la composición: matemáticamente se define el operador de composición o como sigue: si f:x -> Y y g:y-> Z, entonces g o f::x -> Z está dado por (g o f)(x) = g(f(x)). La composición es en sí misma una función que toma dos funciones como parámetros y que produce otra como su valor devuelto. Funciones de este tipo (que tienen parámetros que a su vez son funciones o que producen un resultado que es una función o ambos) se llaman funciones de orden superior.
Resumen de las cualidades de los lenguajes de Programación Funcionales: 1. Todos los procedimientos son funciones y distinguen claramente los valores de entrada (parámetros) de los de salida (resultados) 2. No existen variables ni asignaciones. Las variables han sido reemplazadas por los Parámetros. 3. No existen ciclos, estos han sido reemplazados por llamadas recursivas. 4. El valor de una función depende sólo del valor de sus parámetros y no del orden de Evaluación o de la trayectoria de ejecución que llevó a la llamada. 5. Las funciones son valores de primera clase.
Resumen de las cualidades de los lenguajes de Programación Funcionales: 2 Programación funcional en un lenguaje imperativo El estilo y las técnicas de la programación funcional pueden utilizarse en un lenguaje Imperativo como C, Pascal o Ada, logrando programas con una mejor simplicidad de su semántica y claridad. El requisito básico en cualquier lenguaje de programación es la disponibilidad de la recursión y un mecanismo general de funciones adecuado. El problema típico en la programación de estilo funcional es el costo de llevar a cabo todos los ciclos mediante la recursión. Existe una forma de recursión que es fácil de descubrir durante la interpretación y que puede ser fácilmente convertida a una estructura estándar de ciclo y ésta se llama recursión total, donde la última operación en un procedimiento es llamarse a sí mismo con diferentes argumentos.
Por ej. La versión Funcional del procedimiento gcd tiene recursión total y puede convertirse de manera automática mediante un intérprete en un ciclo, al reasignar los parámetros y volver a empezar Int gcd (int u, int v) { int t1, t2; / * temps introducidos por el traductor For (; ;) { if (v==0) return u; else { t1=v; t2= u / v; u=t1; v= t2; } } } Código en C que da una posible conversión automática de la función gcd de conversión total en el código de ciclo sin recursión
Año:201 - Desafortunadamente muchos usos simples de la recursión no cumplen con la recursividad total. Como consecuencia se han inventado técnicas para convertir funciones a funciones recursivas totales mediante los parámetros de acumulación, que se utilizan para pre-calcular las operaciones que se efectuarán después de la llamada recursiva y pasar los resultados a la llamada recursiva. - Para evitar que se piense que los lenguajes como C o Ada pueden ser utilizados para escribir programas funcionales perfectamente puros, notamos que los lenguajes imperativos contienen varias restricciones que dificultan o imposibilitan la interpretación de todos los programas en este estilo.
Son comunes las siguientes restricciones en los lenguajes imperativos: 1. Los valores estructurados como los arreglos y los registros no pueden ser valores devueltos de las funciones. 2. No existe forma de construir un valor de un tipo estructurado de manera directa. 3. Las funciones no son valores de primera clase, por lo que no es posible escribir funciones de orden superior. Estas restricciones pueden afectar la capacidad de programar en estilo funcional y la forma en la que los métodos funcionales pueden ser simulados utilizando constructores imperativos ocasionales.
Por ejemplo en el estilo funcional, un procedimiento de ordenamiento necesita devolver un arreglo en base a un arreglo de entrada. En un lenguaje como Ada donde los arreglos pueden ser devueltos como valores de las funciones, podemos escribir: function intsort(a: in IntArray) return Int Array is -- escriba IntArray es arreglo (parámetro de eentero <>) de inicio de entero. End intsort; En C sin embargo un arreglo no puede ser el valor devuelto de una función. Además el tamaño de un parámetro de arreglo no puede determinarse en C, por lo que debe ser pasado por separado. Por lo tanto la función Int Sort debe declararse de la sgte manera: void IntSort (int a [ ], int b [ ], int size ) {...... }
3 - Scheme: A fines de la década de 1950 se desarrolló el primer lenguaje que contenía muchas de las características de los lenguajes funcionales modernos. Se conoció como LISP (LISt Procesor) en vista de que su estructura de datos básica es una lista. Existen diversas características de LISP que fueron asociadas a los lenguajes funcionales: 1. La representación uniforme de programas y datos utilizando una sola estructura general de datos (la lista). 2. La definición del lenguaje utilizando un intérprete escrito en el lenguaje mismo. Llamado intérprete meta-circular. 3. La administración automática de toda la memoria por parte del sistema en tiempo de ejecución. Existen dos dialectos de Lisp: Common Lisp, desarrollado en la década del 80 y Scheme realizado a principios de los 70.
4- Elementos de Scheme En Scheme todos los programas y los datos son expresiones y estas son de dos variedades: átomos y listas. Los átomos son como las constantes y los identificadores de un lenguaje imperativo: incluyen los números, las cadenas, los nombres, las funciones y otros constructores. Una lista es simplemente una secuencia de expresiones separadas por espacios y rodeadas por paréntesis. La sintaxis de scheme es particularmente sencilla. expresión -> átomo lista átomo -> número cadena identificador carácter booleano lista -> (expresión secuencia )
Algunos ejemplos de expresiones scheme son los sgtes: 42 -un número hola -una cadena #T - el valor booleano verdadero Atomos # \ a - el carácter a (2.1.2.2.3.1) - una lista de números Lista a hola -otro identificador -otro identificador Identificadores (+ 2 3) - una lista formada por el identificador + y dos números (* (+ 2 3) (/ 6 2)) -una lista formada por un identificador seguido por dos listas Lista
En vista de que los programas de Scheme son expresiones y los programas necesitan ser ejecutados o evaluados, la semántica de scheme está dada por una regla de evaluación para las expresiones. La regla de evaluación estándar para las expresiones Scheme es: 1. Átomos constantes, como números y cadenas que se evalúan a sí mismos. 2. Los identificadores se buscan en el ambiente actual y se reemplazan por el valor allí encontrado (el ambiente en Scheme es esencialmente una tabla de símbolos dinámicamente mantenida que asocia los identificadores con los valores). 3. Cada elemento de la lista se evalúa recursivamente como una expresión (en algún orden no especificado), la primera expresión de la lista debe evaluarse a una función. Esta función se aplica entonces a los valores evaluados del resto de la lista.
Aplicando estas reglas a los ej. Anteriores tendríamos: 42, hello, # T y #\ \a se evalúan a sí mismas ; a y hello son buscados en el ambiente y sus valores son devueltos ( + 2 3) se evalúa buscando el valor de + en el ambiente-devuelve un valor de función Es decir la función de adición que es predefinida y después aplicando esta función de Adición a los valores de 2 y 3 ( ya que las constantes se evalúan a sí mismas ) por lo tanto devuelve el valor 5. De manera similar (* ( + 2 3) (/ 6 2)) se evalúa a 15. La Lista (2. 1.2.2.3.1) por otra parte no puede evaluarse ya que su primera expresión 2. es una constante que no es función. La regla de evaluación de Scheme implica que todas las expresiones deben ser escritas en forma de prefijo