Haskell Language
Listen
Suche…
Syntax
leerer Listenkonstruktor
[] :: [a]Nicht leerer Listenkonstruktor
(:) :: a -> [a] -> [a]head - gibt den ersten Wert einer Liste zurück
head :: [a] -> alast - gibt den letzten Wert einer Liste zurück
last :: [a] -> atail - gibt eine Liste ohne den ersten Eintrag zurück
tail :: [a] -> [a]init - gibt eine Liste ohne den letzten Eintrag zurück
init :: [a] -> [a]xs !! i - gibt das Element an einem Index i in Liste xs zurück
(!!) :: Int -> [a] -> atake n xs - neue Liste mit n ersten Elementen der Liste xs zurückgeben
take :: Int -> [a] -> [a]map :: (a -> b) -> [a] -> [b]
filter :: (a -> Bool) -> [a] -> [a]
(++) :: [a] -> [a]
concat :: [[a]] -> [a]
Bemerkungen
- Der Typ
[a]entspricht[] a. -
[]die leere Liste. -
[]in einer Funktionsdefinition LHS, zBf [] = ..., ist das leere Listenmuster. -
x:xseine Liste, in der der Listexsein Elementxvorangestellt wird -
f (x:xs) = ...ist eine Musterübereinstimmung für eine nicht leere Liste, wobeixder Kopf undxsder Schwanz ist. -
f (a:b:cs) = ...undf (a:(b:cs)) = ...sind gleich. Sie sind eine Musterübereinstimmung für eine Liste von mindestens zwei Elementen, wobei das erste Elementa, das zweite Elementbist und der Rest der Listecs. -
f ((a:as):bs) = ...ist NICHT dasselbe wief (a:(as:bs)) = ...Ersteres ist eine Musterübereinstimmung für eine nicht leere Liste von Listen, wobeiader Kopf des Kopfes ist,aswie der Schwanz des Kopfes, undbsder Schwanz ist. -
f (x:[]) = ...undf [x] = ...sind gleich. Sie sind eine Musterübereinstimmung für eine Liste genau eines Elements. -
f (a:b:[]) = ...undf [a,b] = ...sind gleich. Sie sind eine Musterübereinstimmung für eine Liste von genau zwei Elementen. -
f [a:b] = ...ist eine Musterübereinstimmung für eine Liste genau eines Elements, bei dem das Element auch eine Liste ist.aist der Kopf des Elements undbist der Schwanz des Elements. -
[a,b,c]ist dasselbe wie(a:b:c:[]). Die Standardlistennotation ist nur syntaktischer Zucker für die Konstruktoren(:)und[]. - Sie können
all@(x:y:ys)verwenden, um auf die gesamte Liste alsall(oder einen beliebigen anderen Namenall@(x:y:ys)zu verweisen, anstatt sie(x:y:ys)erneut zu wiederholen.
List Literals
emptyList = []
singletonList = [0] -- = 0 : []
listOfNums = [1, 2, 3] -- = 1 : 2 : [3]
listOfStrings = ["A", "B", "C"]
List Verkettung
listA = [1, 2, 3]
listB = [4, 5, 6]
listAThenB = listA ++ listB -- [1, 2, 3, 4, 5, 6]
(++) xs [] = xs
(++) [] ys = ys
(++) (x:xs) ys = x : (xs ++ ys)
Listen Sie die Grundlagen auf
Der Typkonstruktor für Listen im Haskell-Prelude ist [] . Die Typdeklaration für eine Liste, die Werte des Typs Int wird wie folgt geschrieben:
xs :: [Int] -- or equivalently, but less conveniently,
xs :: [] Int
Listen in Haskell sind homogene Sequenzen , dh alle Elemente müssen vom gleichen Typ sein. Im Gegensatz zu Tupeln ist der Listentyp nicht von der Länge betroffen:
[1,2,3] :: [Int]
[1,2,3,4] :: [Int]
Listen werden mit zwei Konstruktoren erstellt :
[]eine leere Liste.(:), ausgesprochen "cons", fügt Elemente einer Liste hinzu. Consingx(ein Wert vom Typa) aufxs(eine Liste von Werten des gleichen Typa) erzeugt eine neue Liste, dessen Kopf (das erste Element) istx, und der Schwanz (der Rest der Elemente) istxs.
Wir können einfache Listen wie folgt definieren:
ys :: [a]
ys = []
xs :: [Int]
xs = 12 : (99 : (37 : []))
-- or = 12 : 99 : 37 : [] -- ((:) is right-associative)
-- or = [12, 99, 37] -- (syntactic sugar for lists)
Beachten Sie, dass (++) , das zum Erstellen von Listen verwendet werden kann, rekursiv in Form von (:) und [] .
Listen bearbeiten
Um Listen zu verarbeiten, können wir die Übereinstimmungen auf den Konstruktoren des Listentyps einfach kopieren:
listSum :: [Int] -> Int
listSum [] = 0
listSum (x:xs) = x + listSum xs
Wir können mehr Werte abgleichen, indem wir ein ausführlicheres Muster angeben:
sumTwoPer :: [Int] -> Int
sumTwoPer [] = 0
sumTwoPer (x1:x2:xs) = x1 + x2 + sumTwoPer xs
sumTwoPer (x:xs) = x + sumTwoPer xs
Beachten Sie, dass wir im obigen Beispiel eine umfassendere Musterübereinstimmung bereitstellen mussten, um Fälle zu behandeln, in denen eine ungerade Längenliste als Argument angegeben ist.
Das Haskell-Präludium definiert viele eingebaute Ins für die Behandlung von Listen, z. B. map , filter usw. Wenn möglich, sollten Sie diese verwenden, anstatt eigene rekursive Funktionen zu schreiben.
Auf Elemente in Listen zugreifen
Greifen Sie auf das n- te Element einer Liste zu (nullbasiert):
list = [1 .. 10]
firstElement = list !! 0 -- 1
Beachten Sie das !! ist eine Teilfunktion, daher verursachen bestimmte Eingaben Fehler:
list !! (-1) -- *** Exception: Prelude.!!: negative index
list !! 1000 -- *** Exception: Prelude.!!: index too large
Es gibt auch Data.List.genericIndex , eine überladene Version von !! , der einen beliebigen Integral als Index akzeptiert.
import Data.List (genericIndex)
list `genericIndex` 4 -- 5
Wenn sie als einfach verknüpfte Listen implementiert werden, benötigen diese Operationen O (n) Zeit. Wenn Sie häufig Elemente von Index zugreifen, ist es wahrscheinlich besser zu nutzen Data.Vector (aus dem Vektor - Paket) oder anderen Datenstrukturen.
Bereiche
Das Erstellen einer Liste von 1 bis 10 ist mit der Bereichsnotation einfach:
[1..10] -- [1,2,3,4,5,6,7,8,9,10]
Um einen Schritt anzugeben, fügen Sie nach dem Startelement ein Komma und das nächste Element hinzu:
[1,3..10] -- [1,3,5,7,9]
Beachten Sie, dass Haskell den Schritt immer als arithmetische Differenz zwischen Begriffen annimmt und Sie nicht mehr als die ersten beiden Elemente und die obere Grenze angeben können:
[1,3,5..10] -- error
[1,3,9..20] -- error
Um einen Bereich in absteigender Reihenfolge zu generieren, geben Sie immer den negativen Schritt an:
[5..1] -- []
[5,4..1] -- [5,4,3,2,1]
Da Haskell nicht streng ist, werden die Elemente der Liste nur dann ausgewertet, wenn sie benötigt werden. Dies ermöglicht die Verwendung unendlicher Listen. [1..] ist eine unendliche Liste, beginnend mit 1. Diese Liste kann an eine Variable gebunden oder als Funktionsargument übergeben werden:
take 5 [1..] -- returns [1,2,3,4,5] even though [1..] is infinite
Seien Sie vorsichtig bei der Verwendung von Bereichen mit Fließkommawerten, da Überschreitungen bis zum halben Delta akzeptiert werden, um Rundungsprobleme abzuwenden:
[1.0,1.5..2.4] -- [1.0,1.5,2.0,2.5] , though 2.5 > 2.4
[1.0,1.1..1.2] -- [1.0,1.1,1.2000000000000002] , though 1.2000000000000002 > 1.2
Bereiche arbeiten nicht nur mit Zahlen, sondern mit jedem Typ, der die Typklasse Enum implementiert. Bei einigen aufzählbaren Variablen a , b , c die Range-Syntax dem Aufruf dieser Enum Methoden:
[a..] == enumFrom a
[a..c] == enumFromTo a c
[a,b..] == enumFromThen a b
[a,b..c] == enumFromThenTo a b c
Zum Beispiel bei Bool
[False ..] -- [False,True]
Beachten Sie das Leerzeichen nach False , um zu verhindern, dass dies als Modulnamenqualifikation analysiert wird (dh False.. würde als . Von einem Modul False analysiert werden).
Grundfunktionen in Listen
head [1..10] -- 1
last [1..20] -- 20
tail [1..5] -- [2, 3, 4, 5]
init [1..5] -- [1, 2, 3, 4]
length [1 .. 10] -- 10
reverse [1 .. 10] -- [10, 9 .. 1]
take 5 [1, 2 .. ] -- [1, 2, 3, 4, 5]
drop 5 [1 .. 10] -- [6, 7, 8, 9, 10]
concat [[1,2], [], [4]] -- [1,2,4]
foldl
So wird die linke Falte implementiert. Beachten Sie, wie die Reihenfolge der Argumente in der Schrittfunktion im Vergleich zu foldr (der rechten Falz) foldr :
foldl :: (b -> a -> b) -> b -> [a] -> b
foldl f acc [] = acc
foldl f acc (x:xs) = foldl f (f acc x) xs -- = foldl f (acc `f` x) xs
Die linke Falte foldl links. Das ist:
foldl (+) 0 [1, 2, 3] -- is equivalent to ((0 + 1) + 2) + 3
Der Grund ist, dass foldl folgendermaßen bewertet wird ( foldl den induktiven Schritt von foldl ):
foldl (+) 0 [1, 2, 3] -- foldl (+) 0 [ 1, 2, 3 ]
foldl (+) ((+) 0 1) [2, 3] -- foldl (+) (0 + 1) [ 2, 3 ]
foldl (+) ((+) ((+) 0 1) 2) [3] -- foldl (+) ((0 + 1) + 2) [ 3 ]
foldl (+) ((+) ((+) ((+) 0 1) 2) 3) [] -- foldl (+) (((0 + 1) + 2) + 3) []
((+) ((+) ((+) 0 1) 2) 3) -- (((0 + 1) + 2) + 3)
Die letzte Zeile entspricht ((0 + 1) + 2) + 3 . Dies liegt daran, dass (fab) im Allgemeinen dasselbe wie (a `f` b) , und dass ((+) 0 1) im (a `f` b) das gleiche wie (0 + 1) .
foldr
So wird die richtige Falte implementiert:
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f z [] = z
foldr f z (x:xs) = f x (foldr f z xs) -- = x `f` foldr f z xs
Die rechte Falte foldr nach rechts. Das ist:
foldr (+) 0 [1, 2, 3] -- is equivalent to 1 + (2 + (3 + 0))
Der Grund ist, dass foldr folgendermaßen bewertet wird (siehe den induktiven Schritt von foldr ):
foldr (+) 0 [1, 2, 3] -- foldr (+) 0 [1,2,3]
(+) 1 (foldr (+) 0 [2, 3]) -- 1 + foldr (+) 0 [2,3]
(+) 1 ((+) 2 (foldr (+) 0 [3])) -- 1 + (2 + foldr (+) 0 [3])
(+) 1 ((+) 2 ((+) 3 (foldr (+) 0 []))) -- 1 + (2 + (3 + foldr (+) 0 []))
(+) 1 ((+) 2 ((+) 3 0)) -- 1 + (2 + (3 + 0 ))
Die letzte Zeile entspricht 1 + (2 + (3 + 0)) , da ((+) 3 0) dasselbe wie (3 + 0) .
Verwandlung mit "map"
Oft möchten wir den Inhalt einer Sammlung (eine Liste oder etwas Durchlaufbares) konvertieren oder umwandeln. In Haskell benutzen wir die map :
-- Simple add 1
map (+ 1) [1,2,3]
[2,3,4]
map odd [1,2,3]
[True,False,True]
data Gender = Male | Female deriving Show
data Person = Person String Gender Int deriving Show
-- Extract just the age from a list of people
map (\(Person n g a) -> a) [(Person "Alex" Male 31),(Person "Ellie" Female 29)]
[31,29]
Filtern mit "Filter"
Gegeben eine Liste:
li = [1,2,3,4,5]
Wir können eine Liste mit einem Vergleichselement filter :: (a -> Bool) -> [a] -> [a] :
filter (== 1) li -- [1]
filter (even) li -- [2,4]
filter (odd) li -- [1,3,5]
-- Something slightly more complicated
comfy i = notTooLarge && isEven
where
notTooLarge = (i + 1) < 5
isEven = even i
filter comfy li -- [2]
Natürlich geht es nicht nur um Zahlen:
data Gender = Male | Female deriving Show
data Person = Person String Gender Int deriving Show
onlyLadies :: [Person] -> Person
onlyLadies x = filter isFemale x
where
isFemale (Person _ Female _) = True
isFemale _ = False
onlyLadies [(Person "Alex" Male 31),(Person "Ellie" Female 29)]
-- [Person "Ellie" Female 29]
Zippen und Entpacken von Listen
zip nimmt zwei Listen und gibt eine Liste der entsprechenden Paare zurück:
zip [] _ = []
zip _ [] = []
zip (a:as) (b:bs) = (a,b) : zip as bs
> zip [1,3,5] [2,4,6]
> [(1,2),(3,4),(5,6)]
Zwei Listen mit einer Funktion komprimieren:
zipWith f [] _ = []
zipWith f _ [] = []
zipWith f (a:as) (b:bs) = f a b : zipWith f as bs
> zipWith (+) [1,3,5] [2,4,6]
> [3,7,11]
Eine Liste entpacken:
unzip = foldr (\(a,b) ~(as,bs) -> (a:as,b:bs)) ([],[])
> unzip [(1,2),(3,4),(5,6)]
> ([1,3,5],[2,4,6])