Resolviendo problemas en Haskell Gabriela Steren 13 de abril de 2010 Más problemas con listas Otras estructuras de datos Ejercicio 1 Sea el siguiente tipo: type Punto = (Int,Int) Definir, usando listas por comprensión, una lista (infinita) puntosDelPlano::[Punto] que contenga todos los puntos del cuadrante superior derecho del plano. Atención: la función debe garantizar que eventualmente todo punto del cuadrante superior derecho será listado. Una forma de garantizar esto es recorrer los puntos siguiendo las diagonales x + y = k, con k = 0, 1, 2, 3, . . . Ejemplo: [(0,0), (0,1), (1,0), (0,2), (1,1), (2,0), (0,3), (1,2), (2,1), (3,0), (0,4),...] Más problemas con listas Otras estructuras de datos Soluciones Solución 1 puntosDelPlano :: [Punto] puntosDelPlano = [(x, y) | k <-[0..], x <-[0..k], y <-[0..k], x + y == k] Solución 2 puntosDelPlano :: [Punto] puntosDelPlano = [(x,k-x) | k<-[0..], x<-[0..k]] Más problemas con listas Otras estructuras de datos Ejercicio 2 Escribir la función listasQueSuman::Int->[[Int]] que, dado un número natural n, devuelve todas las listas de enteros positivos1 cuya suma sea n. Para este ejercicio se permite usar recursión explı́cita. Solución listasQueSuman::Int->[[Int]] listasQueSuman 0 = [[]] listasQueSuman n | n > 0 = [(x:xs) | x<-[1..n], xs<-listasQueSuman (n-x)] 1 Es decir, mayores o iguales que 1 Más problemas con listas Otras estructuras de datos Ejercicio 3 Definir en Haskell una lista que contenga todas las listas finitas de enteros positivos (esto es, con elementos mayores o iguales que 1). No debe haber listas repetidas. Solución listasPositivas::[[Int]] listasPositivas = [xs | n<-[0..], xs<-listasQueSuman n] Más problemas con listas Otras estructuras de datos Ejercicio 4 Sea el siguiente tipo: data AB a = Hoja a | Bin (AB a) a (AB a) Ejemplo: miÁrbol = Bin (Hoja 3) 5 (Bin (Hoja 7) 8 (Hoja 1)) Definir el esquema de recursión estructural (fold) para árboles binarios, y dar su tipo. El esquema debe permitir definir las funciones altura, ramas, #nodos, #hojas, espejo, etc. Más problemas con listas Otras estructuras de datos ¿Cómo hacemos? Recordemos el tipo de foldr, el esquema de recursión estructural para listas. foldr :: (a -> b -> b) -> b -> [a] -> b ¿Por qué tiene ese tipo? (Pista: pensar en cuáles son los constructores del tipo [a]). Un esquema de recursión estructural espera recibir un argumento por cada constructor (para saber qué devolver en cada caso), y además la estructura que va a recorrer. El tipo de cada argumento va a depender de lo que reciba el constructor correspondiente. (¡Y todos van a devolver lo mismo!) Más problemas con listas Otras estructuras de datos ¿Cómo hacemos? (Continúa) Miremos bien la estructura del tipo. Estamos ante un tipo inductivo con un constructor no recursivo y un constructor recursivo. ¿Cuál va a ser el tipo de nuestro fold? ¿Y la implementación? Más problemas con listas Otras estructuras de datos Solución foldAB::(a->b)->(b->a->b->b)->AB a -> b foldAB fHoja (Hoja n) = fHoja n foldAB fHoja fBin (Bin t1 n t2) = fBin (foldAB fHoja fBin t1) n (foldAB fHoja fBin t2) Ejercicio para ustedes: definir las funciones altura, ramas, #nodos, #hojas y espejo usando foldAB. Si quieren podemos hacer alguna en el pizarrón. Más problemas con listas Otras estructuras de datos Ejercicio 5 Definir el esquema de recursión estructural para el siguiente tipo: data Num a=>Polinomio a = | | | X Cte a Suma (Polinomio a) (Polinomio a) Prod (Polinomio a) (Polinomio a) Luego usar el esquema definido para escribir la función: evaluar::Num a=>a->Polinomio a->a Más problemas con listas Otras estructuras de datos Solución foldPoli::Num a=>b->(a->b)->(b->b->b)->(b->b->b)->Polinomio a->b foldPoli casoX casoCte casoSuma casoProd pol = case pol of X -> casoX Cte n -> casoCte n Suma p q -> casoSuma (rec p) (rec q) Prod p q -> casoProd (rec p) (rec q) where rec = foldPoli casoX casoCte casoSuma casoProd evaluar::Num a=>a->Polinomio a->a evaluar n = foldPoli n id (+) (*) Más problemas con listas Otras estructuras de datos Algo un poco más complejo... ¿o no? 1 Definir el tipo de datos RoseTree de árboles no vacı́os, donde cada nodo tiene una cantidad indeterminada de hijos. 2 Escribir el esquema de recursión estructural para RoseTree. Es importante escribir primero su tipo. Usando el esquema definido, escribir las siguientes funciones: 3 1 2 3 hojas, que dado un RoseTree, devuelve una lista con sus hojas ordenadas de izquierda a derecha, según su aparición en el RoseTree. distancias, que dado un RoseTree, devuelve las distancias de su raı́z a cada una de sus hojas. altura, que devuelve la altura de un RoseTree (la cantidad de nodos de la rama más larga). Si el RoseTree es una hoja, se considera que su altura es 1. Más problemas con listas Tipo RoseTree data RoseTree a = Rose a [RoseTree a] ¿Cómo hacemos el fold? ¿Hay caso/s base? ¿Cuántos llamados recursivos hay que hacer? Otras estructuras de datos Más problemas con listas Otras estructuras de datos Empecemos por el tipo Recordar: un fold siempre toma un argumento por cada constructor del tipo, y además la estructura que va a recorrer. Los argumentos que corresponden a los constructores devuelven siempre algo del tipo que queremos devolver, y reciben tantos argumentos comos sus respectivos constructores. Si el constructor es recursivo, el argumento correspondiente del fold va a recibir el resultado de cada llamada recursiva. Más problemas con listas Otras estructuras de datos ¿Cómo se aplica esto al RoseTree? data RoseTree a = Rose a [RoseTree a] En el caso del RoseTree, eso no cambia. Si nuestro constructor toma una lista de RoseTrees, tendremos una lista de resultados de las recursiones. Entonces el tipo del fold es... foldRT::(a->[b]->b)->RoseTree a->b Más problemas con listas Otras estructuras de datos Ahora implementémoslo foldRT::(a->[b]->b)->RoseTree a->b foldRT f (Rose x hs) = f x (map (foldRT f) hs) Más problemas con listas Otras estructuras de datos Y finalmente... 1 Definir el tipo de datos RoseTree de árboles no vacı́os, donde cada nodo tiene una cantidad indeterminada de hijos. X 2 Escribir el esquema de recursión estructural para RoseTree. Es importante escribir primero su tipo. X Usando el esquema definido, escribir las siguientes funciones: 3 1 2 3 hojas, que dado un RoseTree, devuelve una lista con sus hojas ordenadas de izquierda a derecha, según su aparición en el RoseTree. distancias, que dado un RoseTree, devuelve las distancias de su raı́z a cada una de sus hojas. altura, que devuelve la altura de un RoseTree (la cantidad de nodos de la rama más larga). Si el RoseTree es una hoja, se considera que su altura es 1. Más problemas con listas Otras estructuras de datos Soluciones (prueben resolverlo primero) hojas::RoseTree a->[a] hojas = foldRT (\x rs->if (null rs) then [x] else concat rs) distancias :: RoseTree a -> [Int] distancias = foldRT (\x rs->if (null rs) then [0] else map (+1) (concat rs)) altura::RoseTree a->Int altura = foldRT (\x rs->if (null rs) then 1 else 1+(maxlista rs)) where maxlista = foldr max 0 O, alternativamente... altura = (+1).maxlista.distancias where maxlista = foldr max 0 (Queremos que maxlista [] sea 0 porque las hojas tienen altura 1.) Fin (por ahora) ¿? ¿? ¿? ¿? ¿? ¿? ¿? ¿? ¿? ¿? ¿? ¿? Hay más ejercicios resueltos en el apunte. ¿?