Haskell Language
Lista de Comprensiones
Buscar..
Lista de comprensión básica
Haskell tiene listas de comprensión , que se parecen mucho a las comprensiones de conjunto en matemáticas e implementaciones similares en lenguajes imperativos como Python y JavaScript. En su forma más básica, las listas de comprensión toman la siguiente forma.
[ x | x <- someList ]
Por ejemplo
[ x | x <- [1..4] ] -- [1,2,3,4]
Las funciones también se pueden aplicar directamente a x:
[ f x | x <- someList ]
Esto es equivalente a:
map f someList
Ejemplo:
[ x+1 | x <- [1..4]] -- [2,3,4,5]
Patrones en Expresiones de Generador
Sin embargo, x
en la expresión del generador no es solo variable, sino que puede ser cualquier patrón. En casos de desajuste de patrón, el elemento generado se omite y el procesamiento de la lista continúa con el siguiente elemento, actuando así como un filtro:
[x | Just x <- [Just 1, Nothing, Just 3]] -- [1, 3]
Un generador con una variable x
en su patrón crea un nuevo alcance que contiene todas las expresiones a su derecha, donde x
se define como el elemento generado.
Esto significa que los guardias pueden codificarse como
[ x | x <- [1..4], even x] ==
[ x | x <- [1..4], () <- [() | even x]] ==
[ x | x <- [1..4], () <- if even x then [()] else []]
Guardias
Otra característica de las listas de comprensión son los guardias, que también actúan como filtros. Los guardias son expresiones booleanas y aparecen en el lado derecho de la barra en una lista de comprensión.
Su uso más básico es
[x | p x] === if p x then [x] else []
Cualquier variable utilizada en una guarda debe aparecer a su izquierda en la comprensión, o estar dentro del alcance. Asi que,
[ f x | x <- list, pred1 x y, pred2 x] -- `y` must be defined in outer scope
que es equivalente a
map f (filter pred2 (filter (\x -> pred1 x y) list)) -- or,
-- ($ list) (filter (`pred1` y) >>> filter pred2 >>> map f)
-- list >>= (\x-> [x | pred1 x y]) >>= (\x-> [x | pred2 x]) >>= (\x -> [f x])
(el operador >>=
es infixl 1
, es decir, se asocia (está entre paréntesis) a la izquierda). Ejemplos:
[ x | x <- [1..4], even x] -- [2,4]
[ x^2 + 1 | x <- [1..100], even x ] -- map (\x -> x^2 + 1) (filter even [1..100])
Generadores anidados
Las comprensiones de listas también pueden dibujar elementos de varias listas, en cuyo caso el resultado será la lista de todas las combinaciones posibles de los dos elementos, como si las dos listas se procesaran de forma anidada . Por ejemplo,
[ (a,b) | a <- [1,2,3], b <- ['a','b'] ]
-- [(1,'a'), (1,'b'), (2,'a'), (2,'b'), (3,'a'), (3,'b')]
Comprensiones paralelas
Con la extensión de lenguaje Parallel List Comprehensions ,
[(x,y) | x <- xs | y <- ys]
es equivalente a
zip xs ys
Ejemplo:
[(x,y) | x <- [1,2,3] | y <- [10,20]]
-- [(1,10),(2,20)]
Fijaciones locales
Las comprensiones de listas pueden introducir enlaces locales para las variables que contienen algunos valores provisionales:
[(x,y) | x <- [1..4], let y=x*x+1, even y] -- [(1,2),(3,10)]
El mismo efecto se puede lograr con un truco,
[(x,y) | x <- [1..4], y <- [x*x+1], even y] -- [(1,2),(3,10)]
Las comprensiones de let
in list son recursivas, como siempre. Pero los enlaces del generador no lo son, lo que permite el sombreado :
[x | x <- [1..4], x <- [x*x+1], even x] -- [2,10]
Hacer notación
Cualquier lista de comprensión puede ser codificado de manera correspondiente con la lista mónada de do
la notación .
[f x | x <- xs] f <$> xs do { x <- xs ; return (f x) }
[f x | f <- fs, x <- xs] fs <*> xs do { f <- fs ; x <- xs ; return (f x) }
[y | x <- xs, y <- f x] f =<< xs do { x <- xs ; y <- f x ; return y }
Los guardias se pueden manejar usando Control.Monad.guard
:
[x | x <- xs, even x] do { x <- xs ; guard (even x) ; return x }