You are on page 1of 45

Tema 7

Listas y Strings

Listas
Las listas es el tipos de datos predefinido mas desarrollado en Haskell. Una lista es una secuencia de 0 o ms elementos de un mismo tipo. [T] es el tipo de las listas de elementos de tipo T, donde T puede ser cualquier expresin de tipo incluyendo variables (polimorfismo).

Su definicin es equivalente a
data List a = Empty | Cons [a] [] (:) a (List a)

Es un tipo de datos algebraico, polimrfico y recursivo


- 123 -

Prelude> :i : infixr 5 : (:) :: a -> [a] -> [a] data [a] = [] | a : [a]

-- data constructor

[1,2,3] es azcar sintctico para 1:2:3:[] o (1:(2:(3:[])))


type String = [Char] -- tipo sinnimo le son aplicables todas las funciones polimrficas de [a]

est enriquecido con algunas otras funciones monomrficas. "c1c2...cn" es una abreviatura de ['c1','c2',..., 'cn' ].

- 124 -

Instancia de las clases Eq,Ord,Show


Todos los mtodos de cada clase le son aplicables al tipo [a], en particular al tipo String, supuesto que el tipo a (en part. Char) tambin sea instancia de la clase correspondiente. instance Eq a => Eq [a] where []==[] = True (x:xs)==(y:ys) = x==y && xs==ys _ == _ = False

Show [a] tambin requiere Show a y sirve para mostrar 1:2:3:[] de la forma [1,2,3] (sta no es la que se generara con deriving).
Ord [a] tambin requiere Ord a y define orden lexicogrfico.

- 125 -

Orden lexicogrfico sobre listas


instance Ord a => Ord [a] where -- orden lexicogrfico (<=) [] (_:_) = True (<=) [] [] = True (<=) (_:_) [] = False (<=) (x:xs) (y:ys) = x < y || x==y && (<=) xs ys

Cal debe ser el resultado de evaluar las siguientes expresiones? 1) [1,2] < [1,7,6] 2) [5,1] < [5] 3) "casa" < "campo" 4) "cas" < "casa 5) ["campo", "perro"] < ["country, "dog"]

- 126 -

Patrones e induccin sobre listas

Los patrones bsicos para listas son: [] y x:xs Definir inductivamente una funcin f sobre listas consiste en definir su valor para [] f .. [] .. = ..... para una lista x:xs en base al valor de (f .. xs ..) f .. (x:xs) .. = .. (f .. xs ..) .. Pero los patrones se pueden anidar (sin repetir variables): [],(x,y):ps [], [x], x:y:xs etc
- 127 -

Ejemplo: sumapares :: Num a => [(a,a)] -> [a] sumaPares [] = [] sumaPares ((x,y):xs) = (x+y):(sumapares xs) de modo que sumaPares [(1,9),(37,8)] [10,45] sumaPares [(2,3.5),(1.2,3.3)] [5.5,4.5]

- 128 -

Algunas funciones/operadores predefinidas sencillas


length:: [a] -> Int -- Num a => [b] -> a length [] = 0 length (_:xs) = 1 + length xs head:: [a] -> a head (x:_) = x tail:: [a] -> [a] tail (_:xs) = xs last:: [a] -> a last [x] = x last (_:xs) = last xs
- 129 -

init:: [a] -> [a] init [x] = [] init (x:xs) = x : init xs null:: [a] -> Bool null [] = True null (_:_) = False

-- funcin parcial

-- patrn comodn repetido

infixr 5 ++ (++) :: [a] -> [a] -> [a] [] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys)

- 130 -

infixl 9 !! (!!) :: [a] -> Int -> a -- Indexacin de listas: xs !! n es el n-simo elemento de xs -- Induccin sobre los naturales

xs [] (x:_) (_:xs)

!! !! !! !!

n | n<0 = error "Prelude.!!: negative index" _ = error "Prelude.!!: index too large" 0 = x n = xs !! (n-1)

Hugs> [] !! 3 Program error: Prelude.!!: index too large Hugs> "abc" !! 3 Program error: Prelude.!!: index too large
- 131 -

Listas intensionales
Tambin se llaman listas ZF (Zermelo-Frankling) Su definicin es [<expr> | <cualif>_1, <cualif>_2, ..., <cualif>_n] donde cada cualificador <cualif>_i puede ser de dos tipos: generador: <patrn> <- <expresin> Ejemplo: [x*x | x <- [1..10]] [1,4,9,16,25,36,49,64,81,100] filtro: <expresin booleana>

Ejemplo: [x*x | x <- [1..10], mod x 2==1] [1,9,25,49,81]


- 132 -

Combinacin de cualificadores
Se pueden combinar varios generadores [(x,y) | x <-[1..3], y <-[1..2]] [(1,1),(1,2),(2,1),(2,2),(3,1),(3,2)] Los cualificadores (generadores o filtros) pueden usar los valores generados por generadores previos [(x,y) | x <-[1..3], y <-[1..x]] [(1,1),(2,1),(2,2),(3,1),(3,2),(3,3)] [(x,y) | x <- [1..3], y <-[1..x], even x || even y ] [(2,1),(2,2),(3,2)]

- 133 -

Definicin de funciones mediante una lista intensional


sumapares :: Num a => [(a,a)] -> [a] sumapares ps = [x+y | (x,y) <- ps] sumaPares [(1,9),(37,8)] [10,45] sumaPares [(2,3.5),(7,3),(1.2,3.3)]

[5.5,10.0,4.5]

sumaParesOrd :: Num a => [(a,a)] -> [a] sumaParesOrd ps = [x+y | (x,y) <- ps , x<y] sumaParesOrd [(1,9),(37,8)] [10] sumaParesOrd [(2,3.5),(7,3),(1.2,3.3)]

[5.5,4.5]

- 134 -

quicksort :: Ord a => [a] -> [a] -- (quicksort xs) ordena no-decreciente la lista xs quicksort [ ] = [ ] quicksort (x:xs) = quicksort [y | y<-xs, y<x] ++ [x] ++ quicksort [y | y<-xs, y>=x] divisores :: Integral a => a -> [a] divisores x = [z | z <- [1..x`div`2], mod x z == 0] ++ [x] divisores 25 [1,5,25] divisores 43 [1,43] divisores 13467 => [1,3,67,201,4489,13467]
- 135 -

Las funcin map


map:: (a -> b) -> [a] -> [b] map f xs = [ f x | x <- xs ] Definicin alternativa map f [] = [] map f (x:xs) = f x : map f xs Ejemplos 1. map (+1) [1,3,5,7] [2,4,6,8] 2. map reverse ["uno","dos","tres"] ["onu","sod","sert"] 3. sumapares = map (\(x,y)-> x+y) Ejercicio: mc f xs = [y| x<-xs, y<-f x] Qu calcula la funcin mc? Cal es su tipo? Dar varios ejemplos de uso de mc
- 136 -

Las funcin filter


filter:: (a -> Bool) -> [a] -> [a] filter p xs = [ x | x <- xs, p x ] Definicin alternativa filter p [] = [] filter p (x:xs) | p x = x : filter p xs | otherwise = filter p xs Ejemplos 1. filter even [1,2,3,4] [2,4] 2. filter (/='-') "sub-tarea" "subtarea" 3. divisores x = filter ((==0).(x `mod`)) [1..x`div`2] ++ [x]

- 137 -

La funcin zip

zip:: [a] -> [b] -> [(a,b)] zip (x:xs) (y:ys) = (x,y): zip xs ys zip _ _ = []

Hugs> zip [0..] "hola" [(0,'h'),(1,'o'),(2,'l'),(3,'a')] :: [(Integer,Char)]

Hugs> zip [] "hola" [] :: [(a,Char)]

- 138 -

En Prelude.hs su definicin hace uso de una funcin ms general: zipWith:: (a->b->c) -> [a]->[b]->[c] zipWith f (x:xs) (y:ys) = f x y : zipWith f xs ys zipWith _ _ _ = [] zip:: [a] -> [b] -> [(a,b)] zip = zipWith (\x y -> (x,y))

El producto escalar de dos vectores (como listas): prodesc:: Num a =>[a]->[a]->a


prodesc xs ys = sum (map (uncurry (*)) (zip xs ys)) prodesc xs ys = sum (zipWith (*) xs ys)
- 139 -

Funcin que obtiene la lista de todas las posiciones en la que aparece un elemento x (dado) en una lista dada: posiciones:: a =>[a]->[Int] posiciones x xs = [i| (y,i)<-zip xs [0..] , y==x] Funcin que obtiene la primera posicin o un (-1) si el elemento no est primpos:: a =>[a]-> Int primpos x xs = head (posiciones x xs ++ [-1])

Hay algn problema de eficiencia?


Cal es el valor de la expresin zip xs (tail xs)? Para que puede ser til?

- 140 -

Las funciones del estilo fold


Que tienen en comn las siguientes definiciones? sum [] = 0 sum (x:xs) = x + sum xs sum [1,2,3] 6 and [] = True and (x:xs) = x && and xs and [True,False,True]

False

concat [] = [] concat (x:xs) = x ++ concat xs concat [Hola, que , tal]

Hola que tal

- 141 -

sum [x1,x2,...,xn] = x1 + x2 + ... + xn (+0) funcin : (+) elemento neutro: 0 and [b1,b2,...,bn] = b1 && b2 && ... && bn (&& True) funcin : (&&) elemento neutro: True concat [xs1,xs2,...,xsn] = xs1 ++ xs2 ++ ... ++ xsn (++[]) funcin : (++) elemento neutro: []

- 142 -

foldr ( ) e [x1,x2,...,xn] = x1

(x2

...(xn

e)...)

foldr:: (a -> b -> b) -> b -> [a] -> b foldr f e [] = e foldr f e (x:xs) = f x (foldr f e xs) concat:: [[a]] -> [a] concat = foldr (++) [] and, or:: [Bool] -> Bool and = foldr (&&) True or = foldr (||) False sum, product:: Num a => [a] -> a sum = foldr (+) 0 product = foldr (*) 1
- 143 -

Ejemplo de uso de foldr: funcin unzip


Prelude> unzip [('a',5),('b',3),('d',8)] ("abd",[5,3,8]) :: ([Char],[Integer]) unzip:: [(a,b)] -> ([a],[b]) unzip= foldr (\(x,y)(xs,ys) -> (x:xs, y:ys)) ([],[]) o bien: unzip = foldr conspar ([],[]) where conspar (x,y)(xs,ys) = (x:xs, y:ys)

- 144 -

Dificultad de uso de foldr


Funcin que calcula el mximo de una lista: maxlista :: (Ord a, Bounded a) => [a] -> a maxlista = foldr max minBound class Bounded a where -- instances: Char, Int, Bool minBound :: a maxBound :: a Main> let lis=[5,2,4,6,1]::[Int] in maxlista lis 6 :: Int Main> maxlista [5,2,4,6,1] ERROR - Unresolved overloading *** Type : (Num a, Bounded a, Ord a) => a *** Expression : maxlista [5,2,4,6,1]
- 145 -

foldr1
para listas no vacas (no precisa elemento neutro): foldr1 ( ) [x1,x2,...,xn] = x1 ( x2 ...(x(n-1)

xn)...)

foldr1:: (a -> a -> a) -> [a] -> a foldr1 f [x] = x foldr1 f (x:xs) = f x (foldr1 f xs) Ahora podemos redefinir maxlista como: maximum :: Ord a => [a] -> a maximum = foldr1 max

-- es predefinida

- 146 -

Con asociatividad a la izquierda: foldl


decimal [d0,d1,...,dn] = d0*10^n + ... dn-1*10^1 + + dn*10^0 = 10*(d0*10^(n-1) + ... + dn-1) + dn = 10*(10*(d0*10^(n-2) +...+ dn-2)+ dn-1) + dn
...

= 10*(...(10*0 + d0)+...)+ dn-1) + dn = (... ((0 d0) d1) ... dn-1) dn siendo x y = 10*x + y foldl ( ) e [x1,x2,...,xn] = (...((e x1)

x2)....)

xn

decimal :: [Integer] -> Integer decimal = foldl (\x y -> 10*x+y) 0


- 147 -

foldl:: (a -> b -> a) -> a -> [b] -> a foldl f e [] = e foldl f e (x:xs) = foldl f (f e x) xs Cuando la operacin con la que se pliega es asociativa y tiene elemento neutro es equivalente usar foldl o foldr: concat = foldl (++) [] and = foldl (&&) True or = foldl (||) False sum = foldl (+) 0 product = foldl (*) 1

- 148 -

Versin para listas no-vacas : foldl1


foldl1:: (a -> a -> a) -> [a] -> a foldl1 f (x:xs) = foldl f x xs Funciones que calculan el mximo y el mnimo de una lista: maximum, minimum :: Ord a => [a] -> a maximum = foldl1 max minimum = foldl1 min Funcin que calcula la inversa de una lista: reverse:: [a] -> [a] reverse = foldl (\xs x -> x:xs) [] o bien reverse = foldl (flip (:)) []
- 149 -

Listas como instancia de una clase definida por el ususario

instance Visible a => Visible [a] where toString= concat. map toString size = foldr (+) 0 . map size supuesto que class Visible a where toString:: a -> String size:: a -> Int size x = length (toString x)

Tema 8.- Listas y Strings

Curso 2010-11

- 150 -

Obtener sublistas: take, drop, splitAt


take:: take n take _ take n Int -> [a] -> [a] _ | n <= 0 = [] [] = [] (x:xs) = x : take (n-1) xs

sublists :: [a] -> [[a]] sublists xs = [take n xs | n<-[0..length xs]] sublists [1,2,3,4] [[],[1],[1,2],[1,2,3],[1,2,3,4]] drop:: drop n drop _ drop n Int -> [a] -> [a] xs | n <= 0 = xs [] = [] (_:xs) = drop (n-1) xs
- 151 -

splitAt:: Int -> [a] -> ([a], [a]) Definicin intuitiva: splitAt n xs = (take n xs, drop n xs) Para no recorrer dos veces la lista, su definicin es: splitAt n xs | n <= 0 = ([],xs) splitAt _ [] = ([],[]) splitAt n (x:xs) = (x:xs',xs'') where (xs',xs'') = splitAt (n-1) xs
- 152 -

Generalizacin con un predicado : takeWhile, dropWhile, span, break


takeWhile:: (a -> Bool) -> [a] -> [a] takeWhile p [] = [] takeWhile p (x:xs) | p x = x : takeWhile p xs | otherwise = [] dropWhile:: (a -> Bool) -> [a] -> [a] dropWhile p [] = [] dropWhile p (x:xs) | p x = dropWhile p xs | otherwise = x:xs

- 153 -

span:: (a -> Bool) -> [a] -> ([a],[a]) span p [] = ([],[]) span p (x:xs) | p x = (x:ys, zs) | otherwise = ([],x:xs) where (ys,zs) = span p xs que es una forma ms eficiente que: span p xs = (takeWhile p xs, dropWhile p xs) break:: (a -> Bool) -> [a] -> ([a],[a]) break p = span (not . p)
- 154 -

Encontrar el elemento ms comn en una lista


masComun :: Ord a => [a] -> a masComun = head . listaMasLarga . group . quicksort "Encontrarelelementomscomnenunalista"

quicksort
"Eaaacceeeeeilllmmmnnnnnnooorrsstttu"

group
["E","aaa","cc","eeeee","i","lll","mmm","nnnnnn","ooo", "rr","ss","ttt","u","",""]

listaMasLarga
"nnnnnn"

head
'n'
- 155 -

Agrupar en sublistas
group:: Eq a => [a] -> [[a]]
-- group xs divide xs en sublistas de elementos consecutivos -- iguales, por ejemplo: group "Mississippi" -=> ["M","i","ss","i","ss","i","pp","i"]

group

groupBy (==)

groupBy:: (a -> a -> Bool) -> [a] -> [[a]]


-- groupBy p xs agrupa en sublistas los elementos de xs que -- siendo consecutivos satisfacen el predicado p, por ejemplo -- groupBy (<) [1,2,3,0,-1,-3,5,6] => [[1,2,3],[0],[-1],[-3,5,6]]

groupBy p [] groupBy p (x:xs)

= =

[] (x:ys) : groupBy p zs where (ys,zs) = span (p x) xs


- 156 -

La lista ms larga de una lista de listas


listaMasLarga :: [[a]] -> [a] listaMasLarga = maximumBy (\xs ys ->if length xs>=length ys then xs else ys) maximumBy :: (a -> a -> a) -> [a] -> a maximumBy f [] = error "List.maximumBy: empty list" maximumBy f xs = foldl1 f xs listaMasLarga :: [[a]] -> [a] listaMasLarga = foldl1 (\xs ys ->if length xs>=length ys then xs else ys)
- 157 -

Secuencias aritmticas
[n..] es la lista [n,n+1,n+2,... [n..m] es la lista [n,n+1,n+2,...,limit] siendo limit el menor nmero (entero o real) mayor o igual que m.

[5..8]

[5,6,7,8]
[5.7,6.7,7.7,8.7] []

[5.7..8.2] [2..(-3.1)]

[n,m..] es la lista [n,m,m+i,m+2*i,... siendo i= m-n


El incremento i que puede ser positivo, negativo o cero. [1.2,3.4..] [1.2,(-3.4)..] [1,1..] [1.2,3.4,5.6,7.8,10.0,... [1.2,-3.4,-8.0,-12.6,-17.2,...
- 158 -

[1,1,1,...

[n,m..m] es la lista [n,m,m+i,m+2*i,...,limit] siendo i= m-n limit = mayor nmero (entero/real) menor o igual que m.

Hugs> [1,3..8]
[1,3,5,7] Hugs> [1,-3..(-14)]

Hugs> [1,3..9]
[1,3,5,7,9] Hugs> [1,-3..(-11)]

[1,-3,-7,-11]
Hugs> [1,1..8] [1,1,1,...

[1,-3,-7,-11]
Hugs> [1.5,1.5..8.5] [1.5,1.5,1.5,...
- 159 -

Secuencias de elementos de un tipo enumerado

Todo tipo T que sea instancia de la clase Enum cuenta con los mtodos succ y pred, que hacen las veces de (+1) y (-1) respectivamente para construir secuencias de elementos de tipo T data Prueba = A | B | C | D | E | F deriving (Show,Enum)
[C..] [C,D,E,F] [A..D] [A,B,C,D] [A,B..] [A,B,C,D,E,F] [F,D..] [F,D,B]
- 160 -

Ejemplo: Coloreado de Mapas en Haskell


(Gracias a V. Andrade y M. A. Snchez)

Hay que colorear un mapa, de forma que las provincias vecinas nunca coincidan en el color

- 161 -

data Color = Rojo | Verde | Azul deriving (Show,Enum,Eq) data Provincia = Almeria | Cadiz | Cordoba | Granada | Jaen | Huelva | Malaga | Sevilla deriving (Show,Enum,Eq) type Frontera = Provincia -> [Provincia]

- 162 -

frontera p = case p of Almeria -> [Granada] Cadiz -> [Huelva,Sevilla,Malaga] Cordoba -> [Sevilla,Malaga,Jaen,Granada] Granada -> [Malaga,Cordoba,Jaen,Almeria] Jaen -> [Cordoba,Granada] Huelva -> [Cadiz,Sevilla] Malaga -> [Cadiz,Sevilla,Cordoba,Granada] Sevilla -> [Huelva,Cadiz,Malaga,Cordoba] data Mapa = Atlas [Provincia] Frontera andalucia :: Mapa andalucia = Atlas [Almeria .. Sevilla] frontera

- 163 -

colorFras :: Provincia -> [(Provincia,Color)] -> Frontera -> [Color] -- (colorFras prov provcol fra) devuelve la lista de -- todos los colores de las provincias que son fronteras -- de prov segn provCol y fra colorFras prov provCol fra = [col'| (prov',col')<- provCol, elem prov' (fra prov)]

- 164 -

solsColorear :: (Mapa,[Color]) -> [[(Provincia,Color)]] -- "solsColorear(mapa, colores)" da la lista de todas las -- formas posibles de colorear mapa usando colores solsColorear ((Atlas [] _), colores) = [[]]

solsColorear ((Atlas (prov:restoprov) fra), colores) = [(prov,color):restoMapa | restoMapa <- solsColorear ((Atlas restoprov fra), colores), color <- diff colores (colorFras prov restoMapa fra)] where diff xs ys = [x | x <- xs, notElem x ys]

- 165 -

colorear:: (Mapa,[Color]) -> [(Provincia,Color)] colorear = head . solsColorear sol1 = colorear (andalucia, [Rojo .. Azul]) sol2 = colorear (andalucia, [Rojo,Verde]) Main> sol1 [(Almeria,Verde),(Cadiz,Azul),(Cordoba,Azul),(Granada,Rojo ),(Jaen,Verde),(Huelva,Verde),(Malaga,Verde),(Sevilla,Rojo )]

Main> sol2 Program error

- 166 -

You might also like