Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Evaluacion Perezosa
Evaluacion Perezosa
Mauro Jaskelio
Valores y Tipos
Hasta ahora hemos visto los tipos como conjuntos. Por ejemplo los elementos de Integer son los enteros . . . , 2, 1, 0, 1, 2, . . . Consideremos la siguiente expresi on innito :: Integer innito = innito + 1 La expresi on est a bien tipada. Qu e valor del tipo Integer denota innito ? :: Integer
Bottom
Para que toda expresi on tenga un valor en el tipo consideramos que cada tipo posee un valor llamado bottom (escribimos ).
representa el valor indenido.
Por ejemplo, una funci on que no termina devuelve . Notar que no podemos hacer una funci on que decida si su argumento es o no. Decimos que una funci on f es estricta si f = . siete x = 7 (no estricta) sqr x = x x (estricta)
Valores Parciales
El considerar el valor en un tipo algebraico signica que podemos tener valores parciales Listas Parciales 1: Arboles parciales data T = L Int | N T T N Tuplas parciales (, ) (, 4, ) N (L 3) 1:2:
Newtypes
Se puede declarar un nuevo tipo con un solo constructor con un solo argumento usando newtype newtype Par a b = MkPar (a, b ) A diferencia de type, se crea un nuevo tipo Par a b (a, b ) A diferencia de data, el constructor es estricto data Par a b = MkPar (a, b ) MkPar = MkPar
El newtype s olo existe durante la compilaci on. Por lo tanto es m as eciente en su uso de la memoria.
Ejercicios
Enumerar los elementos del tipo de datos Unit y evaluar cu al es el resultado al aplicarlos a f1 , f2 , y f3 . data Unit = Un () f1 x = Un () f2 (Un x ) = Un x f3 (Un ()) = Un () Cu ales de estas funciones son estrictas?
Inducci on Exhaustiva
Dado que podemos tener valores parciales debemos modicar las pruebas por inducci on Agregamos un caso base adicional. Debemos probar P () En este caso estamos realizando inducci on exhaustiva Por ejemplo, probamos xs + + (ys + + zs ) = (xs + + ys ) + + zs por inducci on sobre toda lista (nita o parcial) xs .
Ya hemos probado que la propiedad vale para [ ] y que si vale para xs vale para (x : xs ) Probamos que vale para : + + (ys + + zs ) = =+ + zs = ( + + ys ) + + zs
Otro Ejemplo
Probar reverse (xs + + ys ) = reverse ys + + reverse xs Por inducci on exhaustiva sobre xs . Los casos xs = [ ] y xs = (x : xs ) quedan como ejercicio. Veamos el caso reverse ( + + ys ) = reverse ys + + reverse LI reverse ( + + ys ) = reverse = LD reverse ys + + reverse = reverse ys + +
La propiedad no se cumple para valores parciales! Ejercicio: Probar que no se cumple con xs = e ys = [ 1, 2 ]
Estrategia de Evaluaci on
Al igual que en el -c alculo, tenemos diferentes estrategias de evaluaci on posibles CBV sqr (3 + 4) { def + } sqr 7 = { def sqr } 77 = { def } 49 = = CBN sqr (3 + 4) { def sqr } (3 + 4) (3 + 4) = { def + } 7 (3 + 4) = { def + } 77 = { def } 49
Dos estrategias diferentes siempre llegan al mismo resultado (si ambas terminan) Notar que esto no pasa en los lenguajes imperativos.
Terminaci on
Qu e pasa cuando uno de los dos no termina? CBV fst (0, innito ) = { def innito } fst (0, innito + 1) = fst (0, (innito + 1) + 1) = . . . CBN fst (0, innito ) = { def fst } 0
Cantidad de Reducciones
La cantidad de reducciones var a seg un la estrategia de evaluaci on CBV = fst (sqr 4, sqr 2) { def sqr } fst (4 4, sqr 2) = { def } fst (16, sqr 2) = { def sqr } fst (16, 2 2) = { def } fst (16, 4) = { def fst } 16 = CBN fst (sqr 4, sqr 2) { def fst } sqr 4 = { def } 44 = { def sqr } 16
Cantidad de Reducciones
La cantidad de reducciones var a seg un la estrategia de evaluaci on CBV sqr (3 + 4) { def + } sqr 7 = { def sqr } 77 = { def } 49 = = CBN sqr (3 + 4) { def sqr } (3 + 4) (3 + 4) = { def + } 7 (3 + 4) = { def + } 77 = { def } 49
Reducci on de grafos
En el u ltimo ejemplo CBV es m as eciente porque sqr duplica su argumento. Se puede mejorar la estrategia CBN utilizando grafos en lugar de arboles. sqr (3 + 4) { def sqr }
= { def + } = { def } 49
@
ED
(3 + 4) 7
Evaluaci on Perezosa
Esta estrategia de evaluaci on (CBN + reducci on de grafos) se denomina evaluaci on perezosa. Un argumento se eval ua s olo si es necesario. Incluso si el argumento es necesario, no necesariamente se eval ua por completo (ej: pattern matching). Si un argumento se eval ua s olo se eval ua una u nica vez. La evaluaci on perezosa da lugar a que deniciones aparentemente inecientes no lo sean.
Un poco de magia
Consideremos insertion sort iSort :: Ord a [ a ] [ a ] iSort [ ] = [] iSort (x : xs ) = ins x (iSort xs ) ins :: (Ord t ) t [ t ] [ t ] ins x [ ] = [x ] ins x (y : ys ) | x y = x : (y : ys ) | otherwise = y : ins x ys Queremos encontrar el m nimo de una lista. Ser a eciente la siguiente funci on? minimo xs = head (iSort xs )
Veamos la reducci on de minimo [ 8, 6, 1, 7, 5 ] minimo [ 8, 6, 1, 7, 5 ] head (iSort [ 8, 6, 1, 7, 5 ]) head (ins 8 (ins 6 (ins 1 (ins 7 (ins 5 [ ]))))) head (ins 8 (ins 6 (ins 1 (ins 7 [ 5 ])))) head (ins 8 (ins 6 (ins 1 (5 : ins 7 [ ])))) head (ins 8 (ins 6 (1 : (5 : ins 7 [ ])))) head (ins 8 (1 : ins 6 (5 : ins 7 [ ]))) head (1 : ins 8 (ins 6 (5 : ins 7 [ ]))) 1 La funci on es lineal!
Listas Innitas
La evaluaci on perezosa permite trabajar con estructuras innitas. Ejemplo unos :: [ Int ] unos = 1 : unos unos { def unos } 1 : unos = { def unos } 1 : 1 : unos = { def unos } 1 : 1 : 1 : unos . . . =
En principio el paso 1 y 3 llevan un tiempo innito En Haskell lo escribimos: primos :: [ Int ] primos = cribar [ 2 . . ] cribar :: [ Int ] [ Int ] cribar (p : xs ) = p : cribar [ x | x xs , x mod p 0 ]
Si ejecutamos > primos [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, . . . > take 10 primos [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ] > takeWhile (<10) primos [ 2, 3, 5, 7 ] En la pr actica s olo usamos un n umero nito de primos
Qu e ganamos?
Ganamos modularidad: separamos la generaci on de los datos del control de los mismos.
Listas de exitos
Una t ecnica com un para hacer backtracking en lenguajes perezosos es la lista de exitos. En lugar de buscar una respuesta, generamos una lista con todas las respuestas. La lista vac a se nala la ausencia de respuestas. Si la lista es no vac a, podemos tomar la cabeza de la misma para obtener una respuesta. La evaluaci on perezosa se encarga de que no se haga m as trabajo que lo necesario.
Caminos en un Grafo
Queremos encontrar un camino de un v ertice a otro en un digrafo ac clico. Para eso, encontramos todos los caminos. type Grafo a = [(a, a)] caminos :: Eq a Grafo a a a [[ a ]] caminos g x y | x y = [[ x ]] | otherwise = [ x : r | z vecinos g x , r caminos g z y ] vecinos :: Eq a Grafo a a [ a ] = [] vecinos [ ] vecinos ((a, b ) : xs ) x | x a = b : vecinos xs x | otherwise = vecinos xs x
1
G3 G4
G5 0
g = [(1, 2), (1, 3), (2, 4), (3, 5), (5, 6), (3, 6)]
caminos g 1 6 [ 1 : r | z vecinos g 1, r caminos g z 6 ] [ 1 : r | z [ 2, 3 ], r caminos g z 6 ] [ 1 : r | r caminos g 2 6 ] + + [ 1 : r | r caminos g 3 6 ] [] + + [ 1 : r | r caminos g 3 6 ] [ 1 : r | r [ 3 : s caminos g 5 6 ]] + + ... [[ 1, 3, 5, 6 ]] + + ... El efecto de la lista de exitos es backtracking ante una falla.
Sharing
El modelo de reducci on por grafos permite la programaci on circular Ya vimos un ejemplo simple ones = 1 : ones que corresponde con el grafo 1 : Otro cl asico ejemplo es el siguiente: data Tree = Leaf Int | Node Tree Tree Queremos reemplazar todas las hojas de un arbol por su valor m nimo. Queremos hacerlo en una sola pasada!
Considere el siguiente programa transform :: Tree Tree transform t = rep t (tmin t ) tmin :: Tree Int tmin (Leaf n) = n tmin (Node l r ) = tmin l min tmin r rep :: Tree Int Tree rep (Leaf ) n = Leaf n rep (Node l r ) n = Node (rep l n) (rep r n) El a rbol se recorre dos veces.
Mejorando repmin
C omo implementar transform en una sola pasada? A medida que recorremos el arbol reemplazamos los valores en las hojas por el valor min mo que calcularemos en el futuro. Gracias a la evaluaci on perezosa podemos denir: trace :: ((i , feedback ) (o , feedback )) i o trace f i = o where (o , z ) = f (i , z )
i z f o z
Para resolver nuestro problema necesitamos una funci on con la siguiente especicaci on repmin :: (Tree , Int ) (Tree , Int ) repmin (t , m) = (rep t m, tmin t ) Calculamos: El caso Leaf es: repmin (Leaf n, m) = (rep (Leaf n) m, tmin (Leaf n)) = (Leaf m, n)
C alculo de repmin
Especicaci on: repmin (t , m) = (rep t m, tmin t ) El caso Node : repmin (Node l r , m) = (rep (Node l r ) m, tmin (Node l r )) = (Node (rep l m) (rep r m), tmin l min tmin r ) = (Node l r , nl min nr ) where (l , nl ) = (rep l m, tmin l ) (r , nr ) = (rep r m, tmin r ) = (Node l r , nl min nr ) where (l , nl ) = repmin l m (r , nr ) = repmin r m
Soluci on en un recorrido
Acabamos de calcular la siguiente funci on: repmin :: (Tree , Int ) (Tree , Int ) repmin (Leaf n, m) = (Leaf m, n) repmin (Node l r , m) = (Node l r , nl min nr ) where (l , nl ) = repmin (l , m) (r , nr ) = repmin (r , m) La soluci on en un recorrido es: transform :: Tree Tree transform = trace repmin
Resumen
Todo tipo tiene un valor . Los tipos pueden tener valores parciales. La inducci on debe ser extendida para manejar este caso. La evaluaci on perezosa facilita algunos estilos de programaci on:
Separaci on de Datos y Control Listas de Exitos Programaci on Circular
Referencias
Programming in Haskell. Graham Hutton (2007) Introduction to Functional Programming. Richard Bird (1998) Using circular programs to eliminate multiple traversals of data. Richard Bird. Acta Informatica 1984.