Tabla de Contenidos

Clase 3

Plan para hoy

Algunas soluciones de la clase anterior

Veamos algunas soluciones presentadas Wiki de Scripts Haskell.

Clase

Veamos el ejercicio 18


Defina la función paraTodo: (A→ Bool)→ [A]→ Bool, donde paraTodo.p.xs decide si todos los elementos de la lista xs cumplen con el predicado p.


Nuevamente esta función es el típico caso que se resuelve con un caso base y un caso inductivo

paraTodo :: (a->Bool) -> [a] -> Bool
paraTodo p [] = True
paraTodo p (x:xs) = p x && paraTodo p xs

Notemos que esta función toma como parámetro una función.

Esto es muy poderoso porque permite escribir programas funcionales que toman programas funcionales y generan otros programas funcionales.
Las funciones son ciudadanos de primera categoría no como en otros lenguajes de programación (imperativos).

Definimos rápidamente el predicado noMultiplo p n que indica cuando p no es múltiplo de n.

noMultiplo :: Int -> Int -> Bool
noMultiplo p n = p `mod` n >0

Notamos:

Podríamos haber escrito

noMultiplo' :: (Int,Int) -> Bool
noMultiplo' (p,n) = p `mod` n >0

Sin embargo son diferentes pues noMultiplo permite la aplicación parcial

Main> :t noMultiplo
noMultiplo :: Int -> Int -> Bool
Main> :t noMultiplo 20
noMultiplo 21 :: Int -> Bool
Main> :t noMultiplo 21 2
noMultiplo 21 2 :: Bool

Con estos elementos y la función desdeHasta que ya figura en sus practico8.hs, podemos construir la función primo (ejercicio).

También podemos usar aplicación parcial en los operadores (+, ==, *) usando secciones de los operadores.
Por ejemplo ver que todos los elementos de una lista son 0 lo escribimos como

Main> paraTodo (==0) [0,0,0,0,0]
True
Main> paraTodo (==0) (desdeHasta 0 20)
False
Main> :t (==0)
flip (==) 0 :: Num a => a -> Bool


Veamos ahora una función que “cierra” dos listas en una sola conteniendo pares.
Por ejemplo:

Main> cierra ["Saturnino", "Tamburrini", "Berlin"] [9,8,3]
[("Saturnino",9),("Tamburrini",8),("Berlin",3)]

Esta función requiere inducción en dos listas al mismo tiempo, por lo tanto tenemos el pattern matching tendrá ahora 4 casos.

cierra :: [a] -> [a] -> [(a,a)]
cierra [] [] = []
cierra (x:xs) [] = []
cierra [] (y:ys) = []
cierra (x:xs) (y:ys) = (x,y) : cierra xs ys

Entonces probemos:

Main> cierra ["Saturnino", "Tamburrini", "Berlin"] [9,8,3]
ERROR - Cannot infer instance
*** Instance   : Num [Char]
*** Expression : cierra ["Saturnino","Tamburrini","Berlin"] [9,8,3]

¿Cuál es el problema?
Tenemos un problema de tipos, el tipo de la función no es lo suficientemente general.
Lo corregimos para que sea cierra :: [a] → [b] → [(a,b)].

Usando las ideas de este ejemplo podemos resolver los ejercicios 21 y 22 del práctico 8.


Generalicemos las funciones duplicar y multiplicar que vimos anteriormente.

duplicar :: [Int] -> [Int]
duplicar [] = []
duplicar (x:xs) = x*2 : duplicar xs
multiplicar :: [Int] -> Int -> [Int]
multiplicar [] a = []
multiplicar (x:xs) a = x*a : multiplicar xs a

La estructura es la misma, estamos aplicando una función a cada elemento de la lista.
El ejercicio 24 dice:


a) Defina la función mapear:(A → B) → [A] → [B] que dadas una función f y una lista xs, aplica f a cada elemento de la lista.
Ejemplo: mapear.primo.[1,2,3,4,5,6]=[false,true,true,false,true,false].


Tenemos nuevamente una función de alto orden y su definición es bien sencilla, con los dos casos (vacía y al menos un elemento) alcanza.

mapear :: (a->b) -> [a] -> [b]
mapear f [] = []
mapear f (x:xs) = f x : mapear f xs

Ahora duplicar es un caso particular si usamos la sección del operador *.

Main> mapear (*2) [1,2,3]
[2,4,6]

Podemos definir duplica' sin tener que hablar de la lista!

duplica' = mapear (*2)

Vemos como funciona haciendo los pasos de reducción de duplica' [1,2].

Ejercicios

Anuncios