Haskell Language
Listenverständnisse
Suche…
Grundlegende Listenverständnisse
Haskell hat Listenverständnisse , die sich mit Satzverständnissen in Mathematik und ähnlichen Implementierungen in imperativen Sprachen wie Python und JavaScript vergleichen. In ihrer grundlegendsten Form haben Listenverständnisse die folgende Form.
[ x | x <- someList ]
Zum Beispiel
[ x | x <- [1..4] ] -- [1,2,3,4]
Funktionen können auch direkt auf x angewendet werden:
[ f x | x <- someList ]
Das ist äquivalent zu:
map f someList
Beispiel:
[ x+1 | x <- [1..4]] -- [2,3,4,5]
Muster in Generator-Ausdrücken
x
im Generatorausdruck ist jedoch nicht nur variabel, sondern kann ein beliebiges Muster sein. Bei einer Musterübereinstimmung wird das erzeugte Element übersprungen, und die Verarbeitung der Liste wird mit dem nächsten Element fortgesetzt und wirkt somit wie ein Filter:
[x | Just x <- [Just 1, Nothing, Just 3]] -- [1, 3]
Ein Generator mit einer Variablen x
im Muster erstellt einen neuen Gültigkeitsbereich, der alle Ausdrücke auf der rechten Seite enthält, wobei x
als generiertes Element definiert ist.
Dies bedeutet, dass Schutzeinrichtungen als codiert werden können
[ x | x <- [1..4], even x] ==
[ x | x <- [1..4], () <- [() | even x]] ==
[ x | x <- [1..4], () <- if even x then [()] else []]
Wachen
Ein weiteres Merkmal von Listenverständnissen sind Guards, die auch als Filter dienen. Guards sind boolesche Ausdrücke und erscheinen in einem Listenverständnis auf der rechten Seite der Leiste.
Ihre grundlegendste Verwendung ist
[x | p x] === if p x then [x] else []
Jede Variable, die in einem Guard verwendet wird, muss im Verständnis auf der linken Seite erscheinen oder anderweitig im Gültigkeitsbereich sein. So,
[ f x | x <- list, pred1 x y, pred2 x] -- `y` must be defined in outer scope
das ist äquivalent zu
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])
(Der Operator >>=
ist infixl 1
, dh er infixl 1
links ein). Beispiele:
[ 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])
Verschachtelte Generatoren
Listenverstehen können auch Elemente aus mehreren Listen ziehen. In diesem Fall ist das Ergebnis die Liste jeder möglichen Kombination der beiden Elemente, als ob die beiden Listen verschachtelt verarbeitet würden. Zum Beispiel,
[ (a,b) | a <- [1,2,3], b <- ['a','b'] ]
-- [(1,'a'), (1,'b'), (2,'a'), (2,'b'), (3,'a'), (3,'b')]
Parallele Erkenntnisse
Mit der Spracherweiterung Parallel List Comprehensions ,
[(x,y) | x <- xs | y <- ys]
ist äquivalent zu
zip xs ys
Beispiel:
[(x,y) | x <- [1,2,3] | y <- [10,20]]
-- [(1,10),(2,20)]
Lokale Bindungen
Listenverstehen können lokale Bindungen für Variablen einschließen, die einige Zwischenwerte enthalten:
[(x,y) | x <- [1..4], let y=x*x+1, even y] -- [(1,2),(3,10)]
Dieselbe Wirkung kann mit einem Trick erzielt werden,
[(x,y) | x <- [1..4], y <- [x*x+1], even y] -- [(1,2),(3,10)]
Die let
in-List-Verständnisse sind wie üblich rekursiv. Generatorbindungen sind jedoch nicht möglich, was das Spiegeln ermöglicht:
[x | x <- [1..4], x <- [x*x+1], even x] -- [2,10]
Notation machen
Jedes Listenverständnis kann mit der do
Notation von list monad entsprechend codiert werden .
[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 }
Die Wachen können mit Control.Monad.guard
behandelt werden:
[x | x <- xs, even x] do { x <- xs ; guard (even x) ; return x }