Vous êtes sur la page 1sur 7

Tabla de contenido

3.1
3.2
3.3
3.4

PROGRAMAS COMO FUNCIONES ................................................................................................................................................ 2


EVALUACIN RETRASADA ......................................................................................................................................................... 3
HASKELL: UN LENGUAJE PEREZOSO COMPLETAMENTE CURRY CON SOBRECARGA ................................................................................. 4
LAS MATEMTICAS EN LA PROGRAMACIN FUNCIONAL I: FUNCIONES RECURSIVAS .............................................................................. 7

3. Programacin funcional

La programacin funcional tiene varias ventajas sobre la programacin imperativa.


Incluyen la concepcin de programas como funciones, el trato de las funciones como si fueran datos, la limitacin de efectos
colaterales y el uso de administracin automtica de la memoria.
Como resultado, un lenguaje de programacin funcional tiene gran flexibilidad, es conciso en su notacin y su semntica es
sencilla.
El inconveniente ha sido la ineficiencia en la ejecucin.
Debido a su naturaleza dinmica, estos lenguajes han sido interpretados ms que compilados, resultando en una prdida
sustancial en velocidad de ejecucin.
Existen varias razones por las cuales nunca han llegado a tener la popularidad de otros lenguajes (imperativos u orientados a
objetos):
o Los programadores aprenden a programar con lenguajes imperativos o bien orientados a objetos.
o Los lenguajes orientados a objetos se construyen sobre conceptos imperativos tradicionales.
Existe suficiente razn para estudiar la programacin funcional incluso si uno no espera jams escribir aplicaciones reales en
un lenguaje funcional, y que los mtodos funcionales como la recursin, la abstraccin funcional y las funciones de orden
superior han influido y/o se han convertido en parte de la mayora de los lenguajes de programacin y deberan formar parte
del conjunto de tcnicas de todo programador profesional.

3.1 Programas como funciones

Desde este paradigma, un programa es una funcin matemtica:

El conjunto es el dominio de , el conjunto se conoce como el rango de . La se conoce como variable


independiente y la como variable dependiente.
Si f no est definida para toda x perteneciente a X se dice que f es una funcin parcial. En otro caso es total.
Podemos referirnos a como entrada y a como salida.
El punto de vista funcional de la programacin no hace ninguna distincin entres procedimiento, funcin o programa, sin
embargo, hace siempre distincin entre valores de entrada o de salida.
En los lenguajes de programacin debemos distinguir entre definicin de funcin y aplicacin de funcin:
o La primera se refiere a la forma en que debe de calcularse una funcin utilizando los parmetros formales.
o La segunda se refiere a la llamada a la funcin declarada utilizando parmetros reales.

El punto de vista funcional de la programacin elimina las variables (como apuntadores de memoria) y por lo tanto la
asignacin, a esto se le define como programacin funcional pura.
Una consecuencia de la programacin funcional pura, es que no pueden existir ciclos (bucles), esto es solventado gracias
a la recursividad.
Otra consecuencia de la falta de variables es que no existe el concepto de estado interno de la funcin, por lo tanto, el
valor de cualquier funcin depende solo de los valores de sus argumentos.
La propiedad de una funcin de que su valor slo dependa de los valores de los argumentos (y de sus constantes no
locales) se conoce como transparencia referencial.
Tomemos como ejemplo la funcin rand, que no depende slo de los argumento, sino del estado de la mquina y de
las llamadas anteriores a s misma, por lo tanto nunca podr ser transparente referencialmente.
Como consecuencia una funcin transparente referencialmente que no tenga argumentos siempre devuelve el mismo
valor, por lo que no se diferenciara de una constante.
La carencia de asignacin y la transparencia referencial de la programacin funcional hacen que los programas
2

Longinos Recuero Bustos (http://longinox.blogspot.com)

funcionales sean particularmente simples.


El ambiente de ejecucin asocia nombre slo a valores no a localizaciones de memoria, una vez introducido un nombre
en el ambiente su valor nunca cambia. A esto se le denomina semntica de valor (para distinguirla de la semntica
de almacenamiento o de apuntadores).
En la programacin funcional debemos ser capaces de manipular las funciones en forma arbitraria, pero sin restricciones
arbitrarias. Las funciones mismas deben considerarse como valores que pueden ser calculados por otras funciones y que
tambin pueden ser parmetros de funciones.
A esta generalidad de las funciones en programacin funcional se dice que las funciones son valores de primera clase.
Funciones que tienen parmetros que a su vez son funciones, o producen un resultado que es una funcin, o ambos, se
las denomina funciones de orden superior.
Resumiendo las cualidades de los lenguajes de programacin funcional y de programas funcionales:
o Todos los procedimientos son funciones y distinguen claramente los valores de entrada (parmetros) de los de
salida (resultados).
o No existen variables ni asignaciones. Las variables han sido reemplazadas por los parmetros.
o No existen ciclos. Estos han sido reemplazados por llamadas recursivas.
o El valor de una funcin depende solo del valor de sus parmetros y no del orden de evaluacin o de la
trayectoria de ejecucin que llev a la llamada.
o Las funciones son valores de primera clase.

3.2 Evaluacin retrasada

Un problema importante que se presenta en el diseo y en el uso de los lenguajes funcionales es la distincin entre las
funciones normales y las formas especiales.
En un lenguaje con una regla de evaluacin de orden aplicativo, como son Scheme y ML todos los parmetros a las
funciones definidas por el usuario se evalan en el momento de la llamada, aunque no sea necesario hacerlo.
En el caso de la funcin and, la expresin Scheme (and a b) o la expresin infija a && b en un lenguaje como C o Java
no necesitan evaluar el parmetro b, si a se evala como falso.
Esto se conoce como evaluacin en cortocircuito de las expresiones booleanas y es un ejemplo de la utilidad de
evaluacin retrasada.
En el caso de la funcin if, no es solo un caso de simple utilidad, sino de necesidad: para que la expresin (if a b c)
en Scheme tenga la semntica apropiada, la evaluacin de b y de c debe retrasarse hasta que se conozca el resultado de
a, y con base en lo anterior se evala b o c, pero nunca ambas.
Scheme y ML deben distinguir entre dos clases de funciones predefinidas: aquellas que utilizan la evaluacin estndar y
aquellas que no lo hacen (las formas especiales).
Esto compromete la uniformidad y la extensin de estos lenguajes.
Un posible argumento para restringir la evaluacin de la funcin a la evaluacin de orden aplicativo es que la
semntica (y la implementacin) son ms simples.
Considerando el caso de una llamada a funcin en la que un parmetro puede tener un valor indefinido, como en la
expresin en C ( 1 == 0 ) && ( 1 == 1 / 0 ). En este caso la evaluacin retrasada puede conducir a un resultado
bien definido.
Las funciones con esta propiedad se conocen como no estrictas, y los lenguajes con funciones estrictas son ms fciles de
implementar.
El no ser estricto puede ser una propiedad deseable.
En un lenguaje que tenga valores de funcin es posible retrasar la evaluacin de un parmetro colocndolo dentro de una
cpsula, es decir, una funcin sin parmetros. Como ejemplo tomemos la llamada p( 1, divByZero ) en el siguiente
fragmento de cdigo en C:
typedef int (*IntProc)( void );
int divByZero( void ){ return( 1 / 0 ); }
int p( int x, IntProc y )
{
if( x ) return 1;
else return y();
}

Longinos Recuero Bustos (http://longinox.blogspot.com)

Algunas veces estos procedimientos de cpsulas, como divByZero se conocen como thunks de paso por nombre.
Existen lenguajes de programacin (Scheme), en donde se utiliza un proceso conocido como memoization, en donde el
valor de un objeto retrasado, es almacenado la primera vez que se aplica una funcin al objeto, y entonces, llamadas
subsecuentes a esa funcin, simplemente devuelven el valor anterior en vez de volver a efectuar el clculo.
Este tipo de evaluacin de parmetros se conoce como paso por necesidad.
La evaluacin retrasada en el paso por nombre es til porque permite que los parmetros se queden sin calcular si no es
necesario y permite que formas especiales como if sean definidas como funciones normales.
Sin embargo, el paso por nombre no puede manejar situaciones ms complejas donde dentro de un clculo slo se
necesita una parte de cada parmetro.
El escenario que se ha descrito, junto con el proceso de memoization, se conoce como evaluacin perezosa.
Esta evaluacin perezosa puede lograrse en un lenguaje funcional al requerir que el ambiente de ejecucin evale
expresiones de acuerdo con las reglas que siguen:
o Todos los argumentos a las funciones definidas por el usuario, se retrasan.
o Todas las ligaduras de los nombres locales en expresiones let y letrec, se retrasan.
o Todos los argumentos a las funciones de constructor (como cons), se retrasan.
o Todos los argumentos a otras funciones predefinidas, como las funciones aritmticas +, * y dems, se obligan.
o Todos los argumentos valuados de funcin, se obligan.
o Todas las condiciones en las funciones de seleccin como if y cond, se obligan.
Estas reglas permiten que las operaciones sobre listas largas calculen nicamente la porcin de la lista que resulte
necesaria.
Tambin es posible incluir listas potencialmente infinitas en leguajes con evaluacin perezosa ya que la parte infinita
jams ser calculada, sino solo lo necesario de la lista como se requiera para un clculo en particular.
Para expresar la naturaleza potencialmente infinita de este tipo de listas, aquella que obedecen a las reglas de evaluacin
perezosa a menudo se llaman flujos.
Un flujo puede considerarse como una lista parcialmente calculada donde los elementos restantes pueden seguir siendo
calculados hasta cualquier nmero deseado.
Los flujos son un tema importante en la programacin funcional.
El ejemplo principal de un lenguaje funcional con evaluacin perezosa es Haskell.
La evaluacin perezosa permite un estilo de programacin funcional que nos da la libertad de separar un cmputo en
partes, formadas por procedimientos que generan flujos (generadores) y otros procedimientos que aceptan flujos (filtros), y
a este tipo de programacin lo llamaremos programacin de generadores y filtros.

3.3 Haskell: Un lenguaje perezoso completamente Curry con sobrecarga

Haskell es un lenguaje funcional puro cuyo desarrollo se inicio a finales de los aos 80.
Elabora y amplia una serie de lenguajes perezosos puramente funcionales desarrollados a finales de los aos 70 y 80 por
David A. Turner, y que culminaron en el leguaje de programacin Miranda.
Incluye caractersticas novedosas, incluyendo la sobrecarga de funciones y un mecanismo de nombre mnadas para
ocuparse de efectos colaterales (por ejemplo la E/S).
Reduce la sintaxis al mnimo utilizando indicios internos del programa, incluyendo la regla de disposicin o regla de
diagramado, que utiliza la sangra y el formato de renglones para resolver ambigedades.
Por lo tanto, es raro que un programa Haskell requiera de puntos y comas, parntesis, incluso no existe una sintaxis
especial para las funciones definidas y no es necesario utilizar barras verticales para las funciones definidas utilizando
emparejamiento de patrones.
fact 0 = 1
fact n = n * fact ( n - 1 )

No permite la redefinicin de funciones que ya hayan sido predefinidas.


La creacin de tipos se hace con el operador :: la creacin de listas con el operador : y la concatenacin de listas con el
operador ++.
Haskell tiene un lenguaje completamente Curry:
o Las funciones de Haskell pueden tomar funciones como parmetros y devolver funciones como resultado.
Una funcin que hace ambas cosas o alguna de ellas se llama funcin de orden superior.
o La curricacin (currying) consiste en simular funciones de varios argumentos mediante funciones de orden
superior de un argumento.
4

Longinos Recuero Bustos (http://longinox.blogspot.com)

Las funciones con mltiples argumentos, pueden manejarse de otra forma, un poco ms compleja quizs,
para explotar la capacidad de retornar funciones como resultado o de tomar funciones como parmetros.
Si consideramos la siguiente definicin del tipo de suma:
suma: Z Z Z

Esto se interpreta como, suma es una funcin que recibe un entero, seguido de otro entero y devuelve la
suma de ambos.
Esto se deduce de la notacin utilizada para dar el tipo de la funcin Z Z Z.
Esta forma de denotar los tipos de las funciones se denomina currificada.
La notacin currificada no es simplemente una notacin, agrega poder de expresividad a los tipos de las
funciones.
El smbolo (que se lee implica), asocia hacia la derecha, esto significa que las dos expresiones
siguientes son equivalentes:
suma: Z Z Z
suma: Z (Z Z)

Por lo tanto se puede decir tambin que suma es una funcin que recibe un entero y devuelve una funcin
que a su vez, recibe un entero y devuelve un entero.
Todos los operadores binarios predefinidos estn en forma Curry y pueden ser aplicados parcialmente a
cualquier argumento utilizando parntesis (esto se conoce en Haskell como una seccin):
plus2 = (2 + )
> plus2 3
5

Haskell tiene funciones annimas (explesiones lambda), las cuales representan a lambda con una diagonal invertida:
> (\ x -> x * x ) 3
9

El operador de composicin de funciones en Haskell se identifica mediante el punto:


> ( ( * 3 ) . ( 2 + ) ) 5
21

Haskell tiene listas y tuplas incorporadas, as como sinnimos de tipo y tipos polimrficos definidos por el usuario:
type ListFn a = [a] -> [a]
type Salary = FLoat

Los nombres de tipos y de constructores deben escribirse en maysculas, en tanto que los nombres de funciones y de
valores deben aparecer en minsculas.
Haskell tiene un sistema de notacin especial para operaciones aplicadas sobre listas, conocido como comprensin de
lista, mismo que est diseado para tener una apariencia de definiciones de conjuntos matemticos:
Square_list lis = [ x * x | x <- lis ]
> Square_list [2, 3]
[4, 9]

Longinos Recuero Bustos (http://longinox.blogspot.com)

Haskell es un lenguaje perezoso (con evaluacin perezosa), esto tambin significa que las listas son idnticas a flujos, y son
capaces de ser potencialmente infinitas:
ones = 1 : ones
[n...]
[n, n + 1, ...]
[2, 4, ...]

Para una lista potencialmente infinita, resultan tiles las funciones que calculan la lista en forma parcial:
o take: extrae los primeros n elementos.
o drop: descarta los primeros n elementos.
> take 5 ( drop 4 [ 2 ... ] )
[6, 7, 8, 9, 10]

Cuando una funcin puede utilizarse con diferentes tipos de argumentos se dice que est sobrecargada. La funcin +, por
ejemplo, puede utilizarse para sumar enteros o para sumar reales. La resolucin de la sobrecarga por parte del sistema
Haskell se basa en organizar los diferentes tipos en lo que se denominan clases de tipos (un conjunto de tipos que definen
ciertas funciones).
Consideremos el operador de comparacin ==. Existen muchos tipos cuyos elementos pueden ser comparables, sin
embargo, los elementos de otros tipos podran no ser comparables. Por ejemplo, comparar la igualdad de dos funciones es
una tarea computacionalmente intratable, mientras que a menudo se desea comparar si dos listas son iguales.
Las clases de tipos solucionan ese problema permitiendo declarar qu tipos son instancias de unas clases determinadas y
proporcionando definiciones de ciertas operaciones asociadas con cada clase de tipos:
class Eq a where
(==), (/=), :: a -> a -> Bool
x == y
= not (x /= y)
x /= y
= not (x == y)

Naturalmente, existen clases de tipos adicionales que requieren o dependen de otras funciones. Esta dependencia de las
clases de tipo sobre otras se llama herencia de clase de tipo, y establece una jerarqua de stas.
Para establecer los requerimientos de herencia para cada clase de tipo, Haskell utiliza una notacin similar a la calificacin
de tipo.
class (Ea a, Show a) => Num a where ...

class (Eq a) => Ord a where


compare
:: a -> a -> Ordering
(<), (<=), (>=), (>) :: a -> a -> Bool
max, min
:: a -> a -> a

Como ejemplo de lo anterior, tomemos una instantnea de la jerarqua de clases de tipos numricos en Haskell:

Longinos Recuero Bustos (http://longinox.blogspot.com)

3.4 Las matemticas en la Programacin funcional I: Funciones recursivas

Puede considerarse una funcin como un conjunto de pares


del producto cartesiano
:

Considerar una funcin como un conjunto tiene ciertas ventajas para el estudio de la definicin de funcin en los
lenguajes de programacin:

Este mtodo de definicin de conjunto a veces se conoce como definicin por extensin.
Sin embargo, la definicin ms comn de una funcin se hace mediante una frmula o propiedad, conocida como
definicin por comprensin:

Importante, de aqu en adelante las definiciones dadas no son del texto base, principalmente debido a que en mi
opinin, este apartado del texto base confunde ms que aporta.
En la definicin de una funcin se pueden usar las funciones estndar y las funciones definidas por el usuario. Pero
tambin se puede usar la propia funcin que se define en su definicin. A tal definicin se la llama definicin recursiva.
En Haskell no tenemos posibilidad de definir bucles. La forma de iterar es utilizando la recursividad.
La recursividad se apoya en el principio de induccin. Este principio es ampliamente utilizado en matemticas para
demostrar que se cumple una propiedad para cualquier valor del mbito que se est tratando.
Principio de induccin:
o La afirmacin es cierta para un valor inicial (caso base).
o
ser cierta para un valor
, si es cierta para el valor anterior a , es decir, si es cierta para
entonces lo ser para .
Como ejemplo, podemos utilizar el principio de induccin para definir los nmeros naturales:
o El nmero es natural.
o
es natural si
es natural.
En Haskel:

de forma que

o como un subconjunto

natural 1 = True
natural n = natural (n-1)
> natural 5
True

Aunque aparentemente el principio de induccin funciona, no se ha tenido en cuenta que, el valor tiene que ser mayor
que el caso base. Por lo tanto si lo intentsemos con el valor de -1, la definicin anterior nunca terminara.
Aadiendo el caso anterior, tendramos:
natural 1 = True
natural n
| n > 1
= natural (n-1)
| otherwise = False
> natural (-1)
False

Recomendacin importante: No seguiremos mentalmente la secuencia de llamadas recursivas para resolver los
problemas. Nos centraremos en definir bien el caso base (el primer elemento que cumple la propiedad) y la relacin de un
valor con el anterior.

Longinos Recuero Bustos (http://longinox.blogspot.com)

Vous aimerez peut-être aussi