Resolviendo problemas en Haskell

Anuncio
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.
¿?
Descargar