(++) :: [a]->[a]->[a] []++ys = ys (x:xy)++ys = x:(xs++ys)Anwendung:
?“Hello“++“World“ “HelloWorld“Eigenschaften:
Assoziative Operation mit neutralem Element []. (*)(*) Also z.B.: [1,2]++[]=[1,2].
concat :: [[a]]->[a] concat[] = [] concat(xs:xss) = xs ++ concat xssAnwendung:
?concat [“Hello“,“ “,“World“] “Hello World“Eigenschaften:
concat(xss++yss) = concat xss++concat yss
reverse :: [a]->[a] reverse[] = [] reverse(x:xs) = reverse xs ++ [x] (*)Anwendung:
?reverse [1,2,3] [3,2,1]Eigenschaften:
reverse (reverse xs) = xs [für alle endlichen Listen xs] (**)(*) Diese Implementation hat ein Problem: Da für eine Liste der Länge n n mal (++) ausgewertet wird, und (++) selbst eine lineare Laufzeitkomlexität hat, ist O(n)=n^2. Wir werden noch eine effizientere (lineare Laufzeitkomplexität) Implementation kennen lernen.
length :: [a]->Int length[] = 0 length(x:xs) = 1 + length xsAnwendung:
?length “Text“ 4Eigenschaften:
- length(xs++ys) = length xs + length ys - lineare Laufzeitkomplexität
(!!) :: [a]->Int->a (x:xs)!!0 = x (x:xs)!!(n+1) = xs!!nAnwendung:
?“Text“!!0 ‘T‘Eigenschaften: (*)
- Indexierung beginnt mit 0 - lineare Laufzeit(*) Programmierer, die in imperativen Sprachen viel mit Arrays gearbeitet haben, könnten versucht sein, Listen in Haskell mit "until", "length" und (!!) wie Arrays zu verarbeiten. In funktionalen Sprachen ist dieses Vorgehen allerdings weder idiomatisch, noch geschickt, da length und (!!) eine lineare Laufzeitkomplexität haben (nicht konstant wie bei Arrays in imperativen Sprachen). Meistens finden sich elegantere Lösungen, die ohne length und (!!) auskommen.
take :: Int -> [a] -> [a] take 0 xs = [] take (n+1)[] = [] take (n+1)(x:xs) = x:take n xsAnwendung:
?take 5 “Wurstbrot“ “Wurst“Eigenschaften:
Wenn i>=length xs dann: take i xs = xs
drop :: Int -> [a] -> [a] drop 0 xs = xs drop (n+1)[] = [] drop (n+1)(x:xs) = drop n xsAnwendung:
?drop 5 “Wurstbrot“ “brot“Eigenschaften:
take n xs ++ drop n xs = xs (*)(*) Für alle nicht-negativen n. Für sehr große n (> length xs) ist take n xs = xs und drop n xs=[].
splitAt :: Int->[a]->([a],[a]) splitAt n xs = (take n xs, drop n xs)Anwendung:
?SplitAt 5 “Wurstbrot“ (“Wurst“, “brot“)Eigenschaften:
Tatsächliche Implementation effizienter als die hier Angegebene, kommt mit einer Traversierung aus (hier:2).
[m..n] [m..] (*)Anwendung:
?[‘A‘..‘Z‘] “ABCDEFGHIJKLMNOPQRSTUVWXYZ“ ?[‘Z‘..‘A‘] ““ ?take 5 [1..] (**) [1,2,3,4,5](*) Für Elementtypen mit begrenztem Wertebereich ist [m..] = [m..maxBound], also z.B. ['\0'..] = ['\0'..'\255'] oder [False..] = [False, True].
zip :: [a]->[b]->[(a,b)] zip [] ys = [] zip (x:xs)[] = [] (*) zip (x:xs)(y:ys) = (x,y):zip(xs,ys)Anwendung:
?zip [1..] “Text“ [(1,‘T‘),(2,‘e‘),(3,‘x‘),(4,‘t‘)]Eigenschaften:
length(zip xs ys)=min(length xs)(length ys) (**)(*) Es mag überaschend erscheinen, daß die Definition zip (x:xs) [] = [] anstelle von zip xs [] = [] verwendet wurde. Diese Definition folgt aber dem Idiom, die linken Seiten der Definitionsgleichungen einer Funktion disjunkt zu gestalten. Hier wird also explizit klargestellt, daß der Fall zip [] [] in der ersten, und nicht in der zweiten Definitionsgleichung behandelt wird.
unzip :: [(a,b)]->([a],[b]) unzip = pair(map fst,map snd) (*)Anwendung:
?unzip[(1,‘E‘),(2,‘i‘)] ([1,2],“Ei“)Eigenschaften:
zipp (unzip xys)=xys [zipp ist die uncurried-Version von zip](*) Diese Implementation greift voraus. Wir werden im Abschnitt "Listenoperationen II" sehen, wie die Funktion map arbeitet, und wann man sie benutzen kann.
head :: [a] -> a -- xs!!0 tail :: [a] -> [a] -- ~“drop 1 xs“ (*) last :: [a] -> a -- letztes Element init :: [a] -> [a] -- “pop_back“ (**)(*) "tail xs" ist nur ungefähr gleich "drop 1 xs", da "tail [] = undefined" und "drop 1 [] = []".