Haskell Language
Lijstbegrippen
Zoeken…
Algemene lijstbegrippen
Haskell heeft lijstbegrippen , die veel lijken op setbegrippen in wiskunde en vergelijkbare implementaties in imperatieve talen zoals Python en JavaScript. In hun meest elementaire vorm hebben lijstbegrippen de volgende vorm.
[ x | x <- someList ]
Bijvoorbeeld
[ x | x <- [1..4] ] -- [1,2,3,4]
Functies kunnen ook rechtstreeks op x worden toegepast:
[ f x | x <- someList ]
Dit komt overeen met:
map f someList
Voorbeeld:
[ x+1 | x <- [1..4]] -- [2,3,4,5]
Patronen in generatoruitdrukkingen
x
in de generatoruitdrukking is echter niet alleen variabel, maar kan elk patroon zijn. In geval van patroonafwijking wordt het gegenereerde element overgeslagen en gaat de verwerking van de lijst verder met het volgende element, waardoor het als een filter werkt:
[x | Just x <- [Just 1, Nothing, Just 3]] -- [1, 3]
Een generator met een variabele x
in zijn patroon creëert een nieuw bereik met alle expressies aan de rechterkant, waarbij x
is gedefinieerd als het gegenereerde element.
Dit betekent dat bewakers kunnen worden gecodeerd als
[ x | x <- [1..4], even x] ==
[ x | x <- [1..4], () <- [() | even x]] ==
[ x | x <- [1..4], () <- if even x then [()] else []]
Guards
Een ander kenmerk van lijstbegrippen zijn bewakers, die ook als filters fungeren. Bewakers zijn Booleaanse uitdrukkingen en verschijnen aan de rechterkant van de balk in een lijstbegrip.
Hun meest elementaire gebruik is
[x | p x] === if p x then [x] else []
Elke variabele die in een bewaker wordt gebruikt, moet links van het begrip worden weergegeven of anderszins binnen het bereik vallen. Zo,
[ f x | x <- list, pred1 x y, pred2 x] -- `y` must be defined in outer scope
wat gelijk is aan
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])
(de operator >>=
is infixl 1
, dat wil zeggen dat deze links wordt gekoppeld (tussen haakjes staat). Voorbeelden:
[ 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])
Geneste generatoren
Lijstbegrippen kunnen ook elementen uit meerdere lijsten trekken, in welk geval het resultaat de lijst is van elke mogelijke combinatie van de twee elementen, alsof de twee lijsten op de geneste manier zijn verwerkt. Bijvoorbeeld,
[ (a,b) | a <- [1,2,3], b <- ['a','b'] ]
-- [(1,'a'), (1,'b'), (2,'a'), (2,'b'), (3,'a'), (3,'b')]
Parallelle begrippen
Met de extensie Parallel List Comprehensions ,
[(x,y) | x <- xs | y <- ys]
is gelijk aan
zip xs ys
Voorbeeld:
[(x,y) | x <- [1,2,3] | y <- [10,20]]
-- [(1,10),(2,20)]
Lokale bindingen
Lijstbegrippen kunnen lokale bindingen introduceren voor variabelen die sommige tussentijdse waarden kunnen bevatten:
[(x,y) | x <- [1..4], let y=x*x+1, even y] -- [(1,2),(3,10)]
Hetzelfde effect kan worden bereikt met een truc,
[(x,y) | x <- [1..4], y <- [x*x+1], even y] -- [(1,2),(3,10)]
Het let
in Lijstcomprehensies is recursieve, zoals gebruikelijk. Maar generatorbindingen zijn dat niet, wat schaduwen mogelijk maakt:
[x | x <- [1..4], x <- [x*x+1], even x] -- [2,10]
Notatie
Elke lijstcomprehensie kan dienovereenkomstig worden gecodeerd met de lijst monade do
notatie .
[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 }
De bewakers kunnen worden behandeld met Control.Monad.guard
:
[x | x <- xs, even x] do { x <- xs ; guard (even x) ; return x }