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] -> a
last - gibt den letzten Wert einer Liste zurück
last :: [a] -> a
tail - 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] -> a
take 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:xs
eine Liste, in der der Listexs
ein Elementx
vorangestellt wird -
f (x:xs) = ...
ist eine Musterübereinstimmung für eine nicht leere Liste, wobeix
der Kopf undxs
der 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 Elementb
ist 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, wobeia
der Kopf des Kopfes ist,as
wie der Schwanz des Kopfes, undbs
der 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.a
ist der Kopf des Elements undb
ist 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])