map :: (a->b)->[a]->[b] map f [] = [] map (x:xs) = f x : map f xsAnwendung:
?map ord “Hallo“ [72,97,108,108,111]Eigenschaften:
map id = id map f . map g = map (f.g)(*)(*) Man kann diese Eigenschaft nutzen, um Traversierungen zu sparen. Zumindestens in der Theorie können solche Optimierungen auch von Compilern ausgeführt werden.
filter :: (a->Bool)->[a]->[a] filter p [] = [] filter p (x:xs) = if p x then x : filter p xs else filter p xsAnwendung:
?filter isUpper “AbKUErzungsFImmel“ “AKUEFI“Eigenschaften:
filter p . filter q = filter (p and q) (*) filter p . concat = concat . map(filter p)(*) Auch hier können wieder Traversierungen gespart werden!
[expr(x) | x<-xs, constraint(x)] = ... [expr(x,y) | x<-xs, y<-s, constraints(x,y)] = ... (**) usw.usf.Anwendung:
?[x*x|x<-[1..10],odd x] [1,9,25,49,81] ?[x*y|x<-[1..10],y<-[1,2],odd x] [1,2,3,6,5,10,7,14,9,18] (***)(*) Der Begriff "Mathematische Mengenkonstruktion" ist natürlich Humbug. Ich habe diese Bezeichnung nur deshalb benutzt, weil man in Haskell zur Konstruktion von Listen eine Schreibweise nutzen kann, die der Schreibweise, mit der in der Mathematik üblicherweise Mengen definiert werden, sehr ähnlich ist.
foldr :: (a->b->b)->b->[a]->b foldr f e [] = e foldr f e (x:xs) = f x (foldr f e xs)Anwendung:
?foldr (+) 0 [1,2,3] (*) 6Eigenschaften:
foldr ($) e [x1,x2,..,xn] = x1 $ (x2 $ (..(xn $ e)..)) (**)(*) Üblicherweise wird als Startelement e das neutrale Element von f übergeben. Auf diese Weise akkumuliert sich das Ergebnis über die gesamte Liste beginnend mit dem letzten Listenelement welches von rechts mit dem "neutralen Element" verknüpft wird.
foldl :: (b->a->b)->b->[a]->b foldl f e [] = e foldl f e (x:xs) = foldl f (f e x) xsAnwendung:
?foldl f [] [1,2,3] where f xs x = x:xs (*) [3,2,1]Eigenschaften:
foldl ($) e [x1,x2,..,xn] = (..((e $ x1) $ x2)..) $ xn(*) Here we go! reverse in einer Zeile auf der Command line reingehackert, und zudem ist es noch effizient. Lineare Laufzeitkomplexität im Vergleich zu Quadratischer bei der Implementation im vor-vorhergehdem Abschnitt.
foldl1 :: (a->a->a)->[a]->a foldl1 f (x:xs) = foldl f x xsAnwendung:
?foldl1 max [3,(-2),5,1] (*) 5Eigenschaften:
- foldl1 funktioniert nur auf nicht leeren Listen. - foldr1 existiert äquivalent.(*) max ist eine typische Anwendung für foldl1, da es für max (auf ganzen Zahlen) kein neutrales Element gibt, ist es nicht möglich max irgentwie sinnvoll auf die leere Liste anzuwenden. Hat man mindestens ein Element zur Verfügung kann man sich dieses allerdings schnappen und als "neutrales Element" in die foldl Funktion reinstecken.
scanl :: (b->a->b)->b->[a]->[b] scanl f e [] = [e] scanl f e (x:xs)= e : scanl f (f e x) xs (*)Anwendung:
?scanl (*) 1 [1..5] [1,1,2,6,24,120]Eigenschaften:
Es gibt scanr,scanl1,scanr1 mit den äquivalenten Eigenschaften der Fold-Familie.(*) Anders als bei foldl wird bei scanl das akkumulierte Element bei jedem Zwischenschritt in der Ergebnisliste eingehängt. Die Ergebnisliste hat immer ein Element mehr als die übergebende Liste. Dieses Element ist das Element e welches bei foldl an die erste Stelle der Ergebnisliste gelangt, und bei foldr an die Letzte.