Sei sulla pagina 1di 7

Evaluacin perezosa

8. Evaluacion tardia
La evaluacion tardia (o perezosa) es un tecnica que se vuelve posible una vez que adoptamos una filosofia funcional. Haskell es un ejemplo de lenguaje de evaluacion tardia. En Haskell no hay garantia de que el codigo sea ejecutado en orden (o que siquiera sea ejecutado), pues Haskell solo ejecuta el codigo cuando es requerido. La evaluacion tardia tiene numerosas ventajas as como desventajas. Discutiremos las ventajas aqui, y veremos como lidiar con las desventajas en la siguiente seccion.

8.1. Optimizacion
La evaluacion tarda ofrece un tremendo potencial para optimizaciones. Un compilador de este tipo ve al codigo funcional exactamente como un matematico ve una expresion algebraica. Puede cancelar cosas y prevenir completamente su ejecucion, reacomodar las piezas para mayor eficiencia, incluso reacomodar el codigo de una forma que reduzca los errores, todo esto garantizando que la logica permanecera intacta. Este es el mayor beneficio de representar programas usando estrictamente primitivas formales: como el codigo se adhiere a leyes matematicas, se puede pensar en el matematicamente.

8.2. Abstraccion de estructuras de control


La evaluacion tarda provee un nivel superior de abstraccion que permite implementar cosas que de otra forma seran imposibles. Por ejemplo, considere implentar la estructura de control del Listado 14:
Listado 14: Estructura de control personalizada aMenosQue ( stock . esEuropeo ()) { enviarASEC ( stock ); }

Deseamos ejecutar eviarASEC a menos que el stock sea europeo. Como podramos implementar aMenosQue? Sin evaluacion tarda, necesitariamos alguna tipo de macro, pero en un lenguaje como Haskell no. Podemos implementar aMenosQue como una funcion! (Ve el Listado 15)
Listado 15: Implementacion de la estructura de control con evaluacion tarda void aMenosQue (boolean condicion , List codigo ) { i f (! condicion ) codigo ; }

Nota que codigo nunca es evaluado si la condicion es verdadera. No podemos hacer los mismo en un lenguaje de evaluacion estricta porque los argumentos seran evaluados antes de entrar en aMenosQue.

8.3. Estructuras de datos infinitas


Los lenguajes de evaluacion tarda permiten la definicion de estructuras de datos infinitas, algo que es mucho mas complicado en un lenguaje de evaluacion estricta. Por ejemplo, considera una

lista con los numeros de Fibonacci. Esta claro que no podemos realizar calculos sobre una lista infinita en un tiempo razonable, o guardarla en memoria. En lenguajes de evaluacion estricta como Java, simplemente definimos una function fibonacci que devuelva un miembro particular de la secuencia. En un lenguaje como Haskell podemos abstraerlo aun mas y simplemente definir una lista infinita con los numeros de Fibonacci. Como el lenguaje es de evaluacion tarda, solo las partes necesarias de la lista que son usadas realmente por el programa seran evaluadas. Esto permite abstraer muchos problemas y verlos desde una persepectiva de mas alto nivel (por ejemplo, podemos usar procesamiento de listas sobre una lista infinita).

8.3. Estructuras de datos infinitas


Los lenguajes de evaluacion tarda permiten la definicion de estructuras de datos infinitas, algo que es mucho mas complicado en un lenguaje de evaluacion estricta. Por ejemplo, considera una lista con los numeros de Fibonacci. Esta claro que no podemos realizar calculos sobre una lista infinita en un tiempo razonable, o guardarla en memoria. En lenguajes de evaluacion estricta como Java, simplemente definimos una function fibonacci que devuelva un miembro particular de la secuencia. En un lenguaje como Haskell podemos abstraerlo aun mas y simplemente definir una lista infinita con los numeros de Fibonacci. Como el lenguaje es de evaluacion tarda, solo las partes necesarias de la lista que son usadas realmente por el programa seran evaluadas. Esto permite abstraer muchos problemas y verlos desde una persepectiva de mas alto nivel (por ejemplo, podemos usar procesamiento de listas sobre una lista infinita). Afortunadamente, no todo esta perdido. Los matematicos han trabajado y desarrollado algunos trucos para asegurarse de que porciones del codigo sean ejecutadas en un orden particular en un ambiente funcional. Tenemos lo mejor de los dos mundos! Estas tecnicas incluyen continuaciones, monads, y escritura singular. En este artculo solo veremos las continuaciones. Dejaremos las otras dos para otra ocasion. Es interesante que las continuaciones son utiles para mas cosas que permitir un orden particular de evaluacion. Hablaremos de esto tambien.

Evaluacin Perezosa
Hoy quisiera hablar de una de las caractersticas de Haskell ms fascinantes, la evaluacin perezosa. Y me han entrado ganas de hablar de ello al ver la utilizacin que se da a la evaluacin perezosa en el problema repMin de Richard Bird. El problema viene a ser el siguiente: reemplazar los valores en los nodos de un rbol por el mnimo valor de este mismo rbol, y solo recorriendo el rbol una sola vez. Yo voy a explicarlo con una estructura ms sencilla, sustituir todos los valores de una lista de nmeros por el mnimo valor de esta lista, recorriendo la lista una sola vez. Y esto usando de manera muy inteligente la evaluacin perezosa. Vamos a empezar contando que es la evaluacin perezosa (lazy).

Evaluacin Perezosa
En los lenguajes de programacin tradicionales, lo que tenemos es evaluacin ansiosa (eager). Con este tipo de evaluacin, cuando asignamos un valor a una variable, o pasamos un parmetro a una funcin, se calcula cual es el valor final a asignar o cual es el valor final del parmetro. Por ejemplo:

v = sqrt(2) + 3 * sqrt(5); f( 4 / g( 7 ) ); Antes de asignar nada a v, se llamara a las funciones sqrt con sus parmetros correspondientes, luego se aplicara la expresin matemtica para obtener el valor final a asignar a v. En el caso de la llamada a la funcin f, antes se ha de llamar a la funcin g y dividir entre 4 el resultado de esta llamada. Por eso se denomina evaluacin perezosa, porque se ha de obtener el valor de las expresiones en cuanto el programa se las encuentra. La evaluacin perezosa solo calcula el valor de las expresiones cuando necesita hacer uso de este valor. En el cdigo de ejemplo anterior, en v se guardara como se calcula su valor y no el valor final. Y el parmetro pasado a f sera la formula completa y no el valor de aplicar la expresin. Si resulta que ni la variable v ni el parmetro de la funcin f se usan, pues nunca se llamara a las funciones sqrt y g, ni se realizaran las operaciones matemticas pertinentes. Esto puede suponer una diferencia enorme entre ejecutar con evaluacin perezosa y evaluacin ansiosa. Si tenemos definidas f y g como:

int f( int return } int g( int return }

a ){ 4; a ){ a - 7;

Con evaluacin ansiosa obtendramos un error "division by zero" mientras que con evaluacin perezosa la ejecucin terminara sin error.

ReplaceMin
La primera versin que podemos pensar de replaceMin es la que pongo a continuacin:

replaceMin xs = replace xs (minimum xs) where replace xs m = map (\a -> m) xs Primero se calcula el mnimo de la lista de nmeros xs y luego se sustituye todos los valores de esta lista por el valor mnimo (usando la funcin replace). Esta primera versin recorre la lista dos veces, una vez para calcular el mnimo y una segunda vez para reemplazar los valores.

Y ahora viene lo verdaderamente impresionante. La versin de replaceMin que recorre la lista una sola vez:

replaceMin xs = ys where (ys, m) = rpMin xs m Y como funciona? Gracias a la evaluacin perezosa. Entendiendo lo que hace la funcin rpMin, lograremos entender como funciona. La funcin rpMin tiene dos parmetros, una lista de nmeros xs y un numero m. Lo que hace rpMin es sustituir todos los valores de xs por m (igual que replace) con el aadido de que a la vez va calculando el mnimo de xs. La funcin rpMin devuelve una tupla con la nueva lista con los valores sustituidos y con el mnimo de la antigua lista. La magia ocurre en la manera de llamar a rpMin desde la funcinreplaceMin, como valor a sustituir se le pasa el mnimo que todava no ha calculado rpMin y del cual no necesita saber su valor. Bsicamente la conversacin entre las dos funciones seria: replaceMin: sustituyeme todos los valores de xs por este valor m del que de momento no se nada rpMin: Pero como? Necesitare saber el valor de m para ponerlo en la lista! replaceMin: No hombre, no! Recuerda que nosotros tenemos evaluacin perezosa. rpMin: Bueno, pues tu sabrs cual es el valor de m, ya se lo dirs al que quiera usar la lista resultante. replaceMin: Te vuelves a equivocar! El valor me lo dirs tu, cuando calcules el mnimo de xs.

El valor de m solo ser necesario fuera de la funcin replaceMin, por ejemplo cuando se imprima la lista resultante por pantalla, y para entonces rpMin ya habr calculado el mnimo. Esta sera la definicin de rpMin. Simplemente va sustituyendo los valores de la lista por el valor m pasado como parmetro, y mientras va calculando el mnimo comparando cada valor de la lista con el mnimo hasta ese momento. Y todo recorriendo la lista una sola vez.

rpMin [x] m rpMin (x:xs) m = where (ys, my) = rpMin xs m

= (m:ys,

([m], min

x) my)

Bueno, os dejo pensando en porqu funciona. O podeis probarlo por vosotros mismos en vuestra distribucin de Haskell favorita. Tambin os dejo los enlaces a las versiones de replaceMin para arboles.

QU ES LA EVALUACIN PEREZOSA?

Evaluacin impaciente (eager): el evaluador hace todo lo que puede. Corresponde a llamada por-valor.

Evaluacin perezosa (lazy):

El evaluador hace solamente lo preciso. Corresponde a llamada pornecesidad.

Significa: Haz slo lo que te pida un patrn a la izquierda de una ecuacin o cualificador (where o let).

Es una estrategia de evaluacin que retrasa la evaluacin de una expresin hasta que el valor de esto realmente se requiera (evaluacin no estricta) y que tambin evita evaluaciones repetidas (compartimiento de ciencias informticas). El compartimiento puede reducir la duracin de ciertas funciones por un factor exponencial sobre otras estrategias de evaluacin no estrictas, como la llamada de nombre.

Las ventajas de la evaluacin perezosa incluyen:

El rendimiento aumenta debido a evitacin de clculos innecesarios y evitacin de condiciones de error en la evaluacin de expresiones compuestas.

La capacidad de construir estructura de datos potencialmente infinita

La capacidad de definir estructura de control como abstracciones en vez de como obras primitivistas.

La evaluacin perezosa puede llevar a la reduccin de la huella de memoria, ya que los valores se crean cuando necesario. Sin embargo, con la evaluacin perezosa, es difcil combinarse con rasgos imperativos como la excepcin que se maneja (manejo de la excepcin) y entrada/salida (entrada/salida), porque el pedido de operaciones se hace indeterminado. La evaluacin perezosa puede introducir el agujero espacial. Tambin, la depuracin es difcil.

3.1.- las estrategia de evaluacin perezosa.

ESTRATEGIAS DE PROGRAMACIN PEREZOSA

Para los ejemplos se considera la funcin

mult :: (Int,Int) Int

mult (x,y) = x * y

Evaluacin mediante paso de parmetros por valor (o por ms internos):

mult (1+2,2+3)

= mult (3,5) [por def. de +]

= 3*5 [por def. de mult]

= 15 [por def. de *]

Evaluacin mediante paso de parmetros por nombre (o por ms externos):

mult (1+2,2+3)

= (1+2)*(3+5) [por def. de mult]

= 3*5 [por def. de +]

Evaluacin con lambda expresiones

mult (1+2) (2+3)

= mult 3 (2+3) [por def. de +]

= (y 3*y) (2+3) [por def. de mult]

= (y 3*y) 5 [por def. de +]

= 3*5 [por def. de +]

= 15 [por def. de *]

3.2.- tcnicas de programacin funcional perezosa.

Potrebbero piacerti anche