Listen und Listenfunktionen Listen modellieren Folgen von gleichartigen, gleichgetypten Objekten. Grundlagen der Programmierung 2 A (Listen) Ausdruck im Programm [0,1,2,3,4,5,6,7,8] Haskell: Listen [] Prof. Dr Manfred Schmidt-Schauß leere Liste, (Nil) [’a’, ’b’, ’c’] [[], [0], [1,2]] Sommersemester 2013 [1..] 1 Grundlagen der Programmierung 2 (Listen-B) Listen und Listenfunktionen Erklärung Typ: [Integer]; d.h. Liste von Integer. Typ: [Char]; Liste von Listen; Typ [[Integer]], d.h. eine Liste von Listen von Integer-Objekten. potentiell unendliche Liste der Zahlen 1,2,3,... – 2/64 – Listen zwei Schreibweisen für Listen: [0,1,2] schöne Darstellung Druckbild einer Liste (0 : (1 : (2 : []))) interne Darstellung mit zweistelligem Infix-Listen-Konstruktor : “ ” und dem Konstruktor [] Listenerzeugung Eingebaute, listenerzeugende Funktionen: Ausdruck im Programm [n..] [n..m] [1..10] [n,m..k] Grundlagen der Programmierung 2 (Listen-B) Listendarstellung Listen-Druckbild Erklärung erzeugt die Liste der Zahlen ab n. erzeugt die Liste von n bis m ergibt [1,2,3,4,5,6,7,8,9,10] erzeugt die Liste von n bis k mit Schritten m − n – 3/64 – Grundlagen der Programmierung 2 (Listen-B) – 4/64 – Darstellung von Listen Baum-Bild einer Liste Listen sind aufgebaut mittels zwei Konstruktoren: [] : : Konstante für die leere Liste Zweistelliger Infix-Konstruktor 10 ~ : a : b Linkes Argument a: erstes Element der Liste Rechtes Argument b: Restliste 9 Beispiel für Haskells Listenerzeugung: 8:[] Liste [8] mit dem Element 8 9:(8:[]) Liste [9,8] mit zwei Elementen 9,8 10:(9:(8:[])) Liste [10,9,8] mit drei Elementen 8 : [] entspricht [10, 9, 8] Grundlagen der Programmierung 2 (Listen-B) – 5/64 – Einfache Listenfunktionen Grundlagen der Programmierung 2 (Listen-B) – 6/64 – Beispiel-Funktion: Länge einer Liste Definitionen head (x:xs) tail (x:xs) = x = xs --- extrahiert das erste Element extrahiert die Restliste Auswertungen Prelude> ????? Prelude> ????? Prelude> ????? Prelude> ????? Auswertung bei bereits ausgewerteter Liste Zweiter Fall; [10/x, (9:(8:[]))/xs] length (10:(9:(8:[]))) 1+ (length (9:(8:[]))) Zweiter Fall; [9/x, (8:[])/xs] 1+(1+ (length (8:[]))) Zweiter Fall; [8/x, ([])/xs] 1+(1+ (1+ (length []))) Erster Fall 1+(1+ (1+ (0))) 3 Additionen 3 head [] ←head [1] ←tail [] ←tail [1] ←- Grundlagen der Programmierung 2 (Listen-B) length :: [a] -> Int length [] = 0 length (x:xs) = 1 + (length xs) – 7/64 – Grundlagen der Programmierung 2 (Listen-B) – 8/64 – Funktionen auf Listen: map Funktionen auf Listen: Beispiele map f [] map f (x:xs) map :: (a -> b) -> [a] -> [b] map f [] = [] map f (x:xs) = (f x) : (map f xs) Auswertung von map quadrat (1:(2:[])): Bei vollständiger Auswertung durch den Interpreter: map wendet eine Funktion f auf alle Elemente einer Liste an und konstruiert die Liste der Ergebnisse. map quadrat (1:(2:[])) quadrat 1 : map quadrat(2:[]) 1*1 : map quadrat (2:[]) 1 : map quadrat (2:[]) 1 : (quadrat 2 : map quadrat []) 1 : (2*2 : map quadrat []) 1 : (4 : map quadrat []) 1 : (4 : []) [] und (x:xs) links von =“ sind Muster(Pattern) ” Z.B. Muster (x:xs) und Argument (s:t) ergibt die Ersetzung: [s/x, t/xs] Grundlagen der Programmierung 2 (Listen-B) – 9/64 – Auswertung: Wieviel ist nötig? Zweite Gleichung Erste Gleichung = [1,4] Grundlagen der Programmierung 2 (Listen-B) – 10/64 – *Main> map quadrat [1..10] ←[1,4,9,16,25,36,49,64,81,100] *Main> map quadrat [1..] ←[1,4,9,16,25,36,49,64,81,100,121, .... = n: zahlenAb (n+1) Auswertung (mit Listenerzeuger als Argument) istLeer [1..] verwende zahlenAb istLeer (zahlenAb 1) istLeer (1: zahlenAb (1+1)) Zweite Gleichung von istLeer False Grundlagen der Programmierung 2 (Listen-B) [quadrat/f, 1/x, (2:[])/xs] bei vollst. Auswertung: Listenfunktionen und Listenerzeuger istLeer [] = True istLeer (x:xs) = False zahlenAb n = [] = (f x) : (map f xs) – 11/64 – Der Listenerzeuger [1..] erzeugt soviel von der Liste [1,2,3,4,5, usw., wie von der Listenfunktion benötigt wird. Grundlagen der Programmierung 2 (Listen-B) – 12/64 – Typen von Listenausdrücken Listenfunktion append mapQuadrat xs = map quadrat xs Die folgende Funktion hängt zwei Listen zusammen (genauer: sie konstruiert die Resultat-Liste) *Main> :t mapQuadrat ←mapQuadrat :: forall a. (Num a) => [a] -> [a] append :: [a] -> [a] -> [a] append [] ys = ys append (x:xs) ys = x : (append xs ys) mapLength xs = map length Haskell-Operator für append: ++ (Infix-Operator) Haskell-Schreibweise: [1,2,3] ++ [4,5,6,7] ergibt [1,2,3,4,5,6,7] xs *Main> :t mapLength ←mapLength :: forall a. [[a]] -> [Int] Grundlagen der Programmierung 2 (Listen-B) – 13/64 – Beispiele Grundlagen der Programmierung 2 (Listen-B) – 14/64 – Funktionen auf Listen (2) Filtern von Elementen aus einer Liste: filter :: (a -> Bool) -> [a] -> [a] filter f [] = [] filter f (x:xs) = if (f x) then x : filter f xs else filter f xs *Main> [] ++ [3,4,5] ←[3,4,5] *Main> [0,1,2] ++ [] ←[0,1,2] *Main> [0,1,2] ++ [3,4,5] ←[0,1,2,3,4,5] *Main> [0..10000] ++ [10001..20000] == [0..20000] Beispiele: *Main> filter (< 5) [1..10] ←[1,2,3,4] *Main> filter primzahlq [2..] ←[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61, 67,71,73,79,83,89,97,101,103,107,109,113,127,131,137, 139,149,151,157,163,167,173,179,181,191,193,197,199,211, ←- True Grundlagen der Programmierung 2 (Listen-B) – 15/64 – Grundlagen der Programmierung 2 (Listen-B) – 16/64 – Funktionen auf Listen Auswertungsreihenfolge, Definitionseinsetzung Die ersten n Elemente der Liste xs: take take take take :: Int -> [a] -> [a] 0 _ = [] n [] = [] n (x:xs) = x : (take (n-1) xs) Auswertung von f s1 . . . sn wenn f mittels Pattern (Muster) definiert ist, innerhalb einer Fallunterscheidung: Erster Schritt: diejenigen Argumente soweit auswerten, bis die Fallunterscheidung möglich ist. Zweiter Schritt: Definitionseinsetzung *Main> take 10 [20..40] ←[20,21,22,23,24,25,26,27,28,29] *Main> take 10 [20,23..] ←[20,23,26,29,32,35,38,41,44,47] Grundlagen der Programmierung 2 (Listen-B) – 17/64 – Geschachtelte Pattern – 18/64 – Listen: Auswertung elimdub eliminiert doppelte Vorkommen von benachbarte Vorkommen von Elementen aus Listen: Listen (bzw. Listenargumente) nennt man: einfach ausgewertet: wenn Listen-Fallunterscheidung möglich ist, d.h. [] oder von der Form s : t elimdub [] = [] elimdub [x] = [x] elimdub (x:(y:r)) = if x == y then elimdub (y:r) else x : elimdub (y:r) vollständig ausgewertet: wenn Liste endlich ist und jeder Tail der Liste mindestens einfach ausgewertet und alle Elemente ebenfalls vollständig ausgewertet sind, Beachte das Pattern (x:(y:r)) Grundlagen der Programmierung 2 (Listen-B) Grundlagen der Programmierung 2 (Listen-B) – 19/64 – Grundlagen der Programmierung 2 (Listen-B) – 20/64 – Iterative Prozesse mit Listenargumenten Iterativer Auswertungsprozess zu f Ein iterativer Auswertungsprozess liegt, bei einer rekursiven Funktion f vor, wenn: Bei Verwendung von Listenargumenten: Die folgenden Begriffe sind unverändert: • • • • (f a1 . . . an ) → − (f a01 . . . a0n ) ∗ (2) (2) → − (f a1 . . . an ) ∗ (3) (3) → − (f a1 . . . an ) ∗ → − ...... ∗ ∗ (m) (m) → − (f a1 . . . an ) → − ... (j) und alle ai sind Basiswerte oder komplett ausgewertete, endliche Listen (bei applikativer Reihenfolge der Auswertung.) ∗ linear rekursiv, end-rekursiv (= tail-recursive) Baum-rekursiv geschachtelt Baum-rekursiv (Bei applikativer Reihenfolge der Auswertung) iterativ muss angepasst werden. Grundlagen der Programmierung 2 (Listen-B) – 21/64 – iterative Version fiter von f length_lin xs = length_linr 0 xs length_linr s [] = s length_linr s (x:xs) = length_linr (s+1) xs f und fiter das gleiche berechnen und fiter einen iterativen Prozess erzeugt (unter applikativer R.) für alle Basiswerte und alle komplett ausgewerteten endlichen Listen als Eingaben Grundlagen der Programmierung 2 (Listen-B) – 22/64 – Beispiel: iterative Version von length: fiter ist iterative Version von f Wenn: Grundlagen der Programmierung 2 (Listen-B) – 23/64 – nicht-iterative Version: length [] = 0 length (x:xs) = 1 + length xs Grundlagen der Programmierung 2 (Listen-B) – 24/64 – Linearer (Nicht-iterativer) Prozess zu length Beispiel: iterativer Prozess Beachte: wir benutzen hier die applikative Reihenfolge der Auswertung length lin (9:(8:(7:(6:...(1:[]))))) length linr 0 (9:(8:(7:(6:...(1:[]))))) length linr 1 (8:(7:(6:...(1:[])))) length linr 2 (7:(6:...(1:[]))) length linr 3 (6:...(1:[])) .......... length linr 9 [] length (9:(8:(7:(6:...(1:[]))))) 1+(length (8:(7:(6:...(1:[])))) 1+(1+(length (7:(6:...(1:[]))) 1+(1+(1+(length (6:...(1:[])) .......... (1+(1+(1+(1+(1+(1+(1+(1+(1+0)))))))))) ..... 9 Grundlagen der Programmierung 2 (Listen-B) – 25/64 – Allgemeine Funktionen auf Listen foldl (Linksfaltung) foldr (Rechtsfaltung) foldl :: (a -> b -> a) -> a -> [b] -> a foldl f z [] = z foldl f z (x:xs) = foldl f (f z x) xs eine zweistellige Operation, ein Anfangselement (Einheitselement) und die Liste. foldl ⊗ e [a1 , . . . , an ] entspricht ((. . . ((e ⊗ a1 ) ⊗ a2 ) . . . ) ⊗ an ). foldr :: (a -> b -> b) -> b -> [a] -> b foldr f z [] = z foldr f z (x:xs) = f x (foldr f z xs) foldr ⊗ e [a1 , . . . , an ] entspricht a1 ⊗ (a2 ⊗ (. . . (an ⊗ e))) Grundlagen der Programmierung 2 (Listen-B) – 26/64 – Definitionen der fold-Funktionen Allgemeine Funktionen (Methoden): foldl und foldr Links-Faltung und Rechts-Faltung Die 3 Argumente sind: • • • Grundlagen der Programmierung 2 (Listen-B) – 27/64 – Grundlagen der Programmierung 2 (Listen-B) – 28/64 – Fold-Verwendungen Lambda-Ausdrücke Lokale Funktionsdefinitionen, anonyme Funktionen Summe bzw. Produkt einer Liste von Zahlen: Lambda-Ausdruck sum xs = foldl (+) 0 xs produkt xs = foldl (*) 1 xs concat xs = foldr (++) [] xs \x1 . . . xn -> hAusdrucki x1 , x2 , ... sind die formalen Parameter Beispiel foldl (+) 0 [1,2,3,4] foldr (++) [] [[0],[2,3],[5]] ≡ ≡ ((((0+1)+2)+3)+4) [0] ++ ([2,3] ++ ([5] ++ [])) Je nach Operator ist foldl, oder foldr besser geeignet. Grundlagen der Programmierung 2 (Listen-B) quadrat = \x -> x*x Der Lambdaausdruck kann wie eine Funktion verwendet werden – 29/64 – let und lokale Bindungen Grundlagen der Programmierung 2 (Listen-B) – 30/64 – let und lokale Bindungen let {x1 = s1 ; . . . ; xn = sn } in t {x1 = s1 ; . . . ; xn = sn } t ist eine lokale Umgebung die Variablen xi können in t vorkommen mit der Bedeutung: Wert von si “ ” der eigentliche Ausdruck In Haskell: rekursives let. D.h. xi kann in jedem sj vorkommen Beachte im ghci-Interpreter: Spezielle Verwendung des let let x1 = 5 x2 = "abc" x3 = 7*x1 in (x1,x2,x3) Grundlagen der Programmierung 2 (Listen-B) – 31/64 – Grundlagen der Programmierung 2 (Listen-B) – 32/64 – Erweiterungen des let Freie und Gebundene Variablen Statische Analysen: Untersuche den Programmtext bzw. den Syntaxbaum. Funktionen sind definierbar direkt in einem rekursiven let: Um Definitionen von lokalen Namen korrekt zu handhaben, braucht man neue Begriffe: let {f x1 . . . xn = s; . . .} in t Gültigkeitsbereich einer Variablen x freie Variablen eines Ausdrucks gebundene Variablen eines Ausdrucks Zum Beispiel: let hochdrei x = x*x*x, a = 3 in hochdrei a Grundlagen der Programmierung 2 (Listen-B) – 33/64 – Freie und Gebundene Variablen (2) • • Grundlagen der Programmierung 2 (Listen-B) Grundlagen der Programmierung 2 (Listen-B) – 34/64 – Beispiel Problem: Variablen können mit gleichem Namen, aber verschiedener Bedeutung in einem Ausdruck vorkommen: Lösung: Text-Fragment(e) des Programms in dem dieses x gemeint ist. Variablen, deren Bedeutung außerhalb des Ausdrucks festgelegt wird. Variablen, deren Bedeutung innerhalb des Ausdrucks festgelegt wird. Exakte Festlegung der Gültigkeitsbereiche für jedes syntaktische Konstrukt Umbenennen von gebundenen Variablennamen, falls nötig – 35/64 – \x-> x*x Gültigkeitsbereich von x: der Ausdruck x*x die Variable x ist gebunden von \x x*x in diesem Ausdruck ist x frei (let x = 1, y = 2 in x*y*z) x und y sind gebunden z ist frei Grundlagen der Programmierung 2 (Listen-B) – 36/64 – Definition von FV Beispiel: freie Variablen FV: • • • • • ergibt Menge von Variablen-Namen. F V (x) := {x} , wenn x ein Variablenname ist F V ((s t)) := F V (s) ∪ F V (t) F V (if t1 then t2 else t3 ) := F V (t1 ) ∪ F V (t2 ) ∪ F V (t3 ) F V (\x1 . . . xn -> t) := F V (t) \ {x1 , . . . , xn } F V (let x1 = s1 , . . . , xn = sn in t) := (F V (t) ∪ F V (s1 ) ∪ . . . ∪ F V (sn )) \ {x1 , . . . , xn } • F V (let f x1 . . . xn = s in t) := F V (let f = \x1 . . . xn -> s in t) Beachte: = = = = F V (f x y) \ {x} ... {x, f, y} \ {x} {f, y} FV ist eine Funktion auf dem Syntaxbaum; keine Haskell-Funktion Grundlagen der Programmierung 2 (Listen-B) – 37/64 – Gebundene Variablen GV (t) Grundlagen der Programmierung 2 (Listen-B) – 38/64 – Beispiel : Berechnung von gebundenen Variablen Entsprechend der F V -Definition: • GV (x) := ∅ • GV ((s t)) := GV (s) ∪ GV (t) • GV (if t1 then t2 else t3 ) := GV (t1 ) ∪ GV (t2 ) ∪ GV (t3 ) • GV (\x1 . . . xn -> t) := GV (t) ∪ {x1 , . . . , xn } • GV (let x1 = s1 , . . . , xn = sn in t) := (GV (t) ∪ GV (s1 ) ∪ . . . ∪ GV (sn ) ∪ {x1 , . . . , xn }}) • GV (let f x1 . . . xn = s in t) := GV (let f = \x1 . . . xn -> s in t) = {f, x1 , . . . , xn } ∪ GV (s) ∪ GV (t) Auch hier F V (\x -> (f x y)) GV(\x -> (f x y)) = = = = GV (f x y) ∪ {x} ... ∅ ∪ {x} {x} GV ist eine Funktion auf dem Syntaxbaum; keine Haskell-Funktion Grundlagen der Programmierung 2 (Listen-B) – 39/64 – Grundlagen der Programmierung 2 (Listen-B) – 40/64 – Lexikalischer Gültigkeitsbereich einer Variablen • • let x = sin t \x y z -> t Beispiel die Vorkommen der freien Variablen x in s, t werden gebunden. s, t ist der lexikalische Gültigkeitsbereich der Variablen x Ausdruck t = \x -> (x (\x -> x*x)) x ist in t gebunden, aber in zwei Bindungsbereichen: die freien Variablen x, y, z in t werden gebunden. t ist der lexikalische Gültigkeitsbereich von x, y, z. Umbenennen des gebundenen x in y ergibt: (x (\y -> y*y)) Grundlagen der Programmierung 2 (Listen-B) – 41/64 – Beispiele \x -> (x (\x -> x*x)) In (x (\x -> x*x)) kommt x frei und gebunden vor. Grundlagen der Programmierung 2 (Listen-B) – 42/64 – Beispiel: Reihenfolgenunabhängigkeit Zwei Bindungsbereiche für x in einem let-Ausdruck: let x = 10 in (let x = 100 in (x+x)) + x Umbenennung ergibt: let x1 = 10 in (let x2 = 100 in (x2+x2)) + x1 Dieser Term wertet zu 210 aus. let Beispiel: let x = (x*x) in (x+x) y = 20*z x = 10+y z = 15 in x Wertet aus zu : 310. führt zu Nichtterminierung des Haskell-Interpreters ohne Reduktionen auszuführen. Grundlagen der Programmierung 2 (Listen-B) – 43/64 – Grundlagen der Programmierung 2 (Listen-B) – 44/64 – Beispiel geschachtelte Bindungsbereiche Optimierung mittels let let {x = 1;y = 7} in (let {y = 2; z = 4} in (let z = 5 in (x+y+z))) Vermeidung redundanter Auswertungen mit let f(x,y) := x(1 + xy)2 + y(1-y) + (1+xy)(1-y) optimierbar durch Vermeidung von Doppelauswertungen: x = 1;y = 7 Der zugehörige Ausdruck ist: y = 2; z = 4 let a = 1 + x*y b = 1 - y in x*a*a + y*b + a*b z=5 x+y+z Grundlagen der Programmierung 2 (Listen-B) – 45/64 – Zusammengesetzte Daten-Objekte Grundlagen der Programmierung 2 (Listen-B) – 46/64 – n-Tupel von Objekten n-Tupel sind Verallgemeinerung von Paaren (n ≥ 2) Paar: Beispiele (1, 2) (1, "hallo") (1,(2,"hallo")) Grundlagen der Programmierung 2 (Listen-B) (t1 , . . . , tn ) (. . . , . . .) ist n-Tupel von t1 , . . . , tn (Paar von Zahl und Paar...) – 47/64 – Beispiele (1,2,3,True) (1,(2,True),3) ("hallo",False) (fakultaet 100,\x-> x) Grundlagen der Programmierung 2 (Listen-B) – 48/64 – Zusammengesetzte Objekte: Datentypen Beispiel n-Tupel Für Datentypen benötigt man: n-Tupelkonstruktor t1 , . . . , tn −→ (t1 , . . . , tn ) Datenkonstruktor(en) Datenselektor(en) Beispiel Paarkonstruktor Paarselektoren Eigenschaften: fst(s, t) = s snd(s, t) = t. Tupelselektoren s, t −→ (s, t) fst, snd n-Tupel haben einen impliziten Konstruktor: (., . . . , .) | {z } n und Grundlagen der Programmierung 2 (Listen-B) n Selektoren: pro Stelle ein Selektor – 49/64 – Definition der Selektoren Grundlagen der Programmierung 2 (Listen-B) – 50/64 – Beispiel: Typen von Selektoren, Konstruktoren, Tupeln Muster (pattern) statt Selektoren. Muster sind syntaktisch dort erlaubt, wo formale Parameter (Variablen) neu eingeführt werden: • in Funktionsdefinitionen, • in Lambda-Ausdrücken und • in let-Ausdrücken. Beispiel-Definitionen von Selektoren mittels Muster (1, 1) :: (Integer, Integer) (1, (2, True)) :: (Integer, (Integer, Bool)) (., . . . , .) | {z } :: a1 → a2 → . . . → an → (a1, a2, . . . , an) n fst (x,y) = x snd (x,y) = y selektiere_erstes_von_3 (x1,x2,x3) = x1 selektiere_zweites_von_3 (x1,x2,x3) = x2 selektiere_drittes_von_3 (x1,x2,x3) = x3 Grundlagen der Programmierung 2 (Listen-B) selektiere_drittes_von_3 :: (a1, a2, a3) → a3 – 51/64 – Grundlagen der Programmierung 2 (Listen-B) – 52/64 – Benutzerdefinierte Konstruktoren Muster (pattern) streckenAnfang (Streckenkonstruktor x y) = x Benutzerdefinierte Konstruktoren sind definierbar in Haskell mittels data-Anweisung • • Nutzen der Muster: Beispiel data Punkt = Punktkonstruktor Double Double data Strecke = Streckenkonstruktor Punkt Punkt Punkt, Strecke: Punktkonstruktor Streckenkonstruktor Double, Punkt (rechts) tiefes Selektieren Ersatz für Selektoren Syntax der Muster: Typen Konstruktoren hMusteri ::= hVariablei | (hMusteri) | hKonstruktor(n) i hMusteri . . . hMusteri {z } | n Typen der Argumente | (hMusteri, . . . , hMusteri) Bedingung: in einem Muster darf keine Variable doppelt vorkommen Grundlagen der Programmierung 2 (Listen-B) – 53/64 – Muster; Mustervergleich: – 54/64 – Benutzerdefinierte Typnamen mit Parametern Beispiel Punkt, Strecke, Polygonzug Anpassen des Objekts an das Muster gleichzeitige Selektion mittels impliziter let-Bindungen I.a. vorher Auswertung des Objekts erforderlich data data data data Beispiele (x,y,(u,v)) anpassen an: (1,2,(3,4)) ergibt: let x = 1;y = 2;u = 3;v = 4 in ... (x,y,(u,v)) anpassen an: (1,2,True) Punkt a Strecke a Vektor a Polygon a = = = = Punkt a a Strecke (Punkt a) (Punkt a) Vektor a a Polygon [Punkt a] Typ und Konstruktor können gleiche Namen haben. Der Parameter a kann jeder Typ sein: z.B.: Float, Int, aber auch [[(Int, Char)]] ergibt: Fehler. Kann nicht vorkommen wegen Typcheck. (x,y,u) anpassen an: (1,2,(4,5)) ergibt: let x = 1; y = 2;u = (4,5) in ... Grundlagen der Programmierung 2 (Listen-B) Grundlagen der Programmierung 2 (Listen-B) – 55/64 – Grundlagen der Programmierung 2 (Listen-B) – 56/64 – Funktionen auf Punkt, Strecke, Polygonzug Summentypen und Fallunterscheidung Summentypen: diese haben mehr als einen Konstruktor Beispiele: Bool mit True False addiereVektoren::Num a => Vektor a -> Vektor a -> Vektor a data addiereVektoren (Vektor a1 a2) (Vektor b1 b2) = Vektor (a1 + b1) (a2 + b2) Bool = True | False Aufzählungstyp: streckenLaenge (Strecke (Punkt a1 a2) (Punkt b1 b2)) = data Farben = Rot | Gruen | Blau | Weiss | Schwarz sqrt (fromInteger ( (quadrat (a1-b1)) + (quadrat (a2-b2)))) data Kontostand = Grundlagen der Programmierung 2 (Listen-B) – 57/64 – Liste als Summentyp Euro Integer | Dollar | SFranken Integer Integer Grundlagen der Programmierung 2 (Listen-B) – 58/64 – Fallunterscheidung mit case selbstdefinierte Listen: (sogar rekursiv definierter Typ) Syntax: case hAusdrucki of Typvariable data Liste a = Nil | Cons a (Liste a) Typkonstruktor Einschränkung: Kontextbedingung: Typ erstes Arg Konstruktor1 Typ zweites Arg {h Musteri -> hAusdrucki; ...; h Musteri -> hAusdrucki} nur einfache Muster: K x1 . . . xn die Muster müssen vom Typ her passen. Beispiel: Konstruktor2 und x y = case x of True -> y; False -> False Listen-Definition in Haskell: data [a] = [] | a : [a] Grundlagen der Programmierung 2 (Listen-B) – 59/64 – Grundlagen der Programmierung 2 (Listen-B) – 60/64 – case: Gültigkeitsbereich und Beispiel Reduktionsregel zum case F V (case x of True -> y; False -> False) = {x,y} F V (case x of (Punkt u v) -> u) = {x} GV (case x of (Punkt u v) -> u) = {u,v} Grundlagen der Programmierung 2 (Listen-B) case-Reduktion – 61/64 – Beispiel-Verwendung (Entzuckerung) map f [] map f (x:xs) (zusätzliche Reduktionsregel) (case (c t1 . . . tn ) of . . . (c x1 . . . xn → s) . . .) s[t1 /x1 , . . . , tn /xn ] Grundlagen der Programmierung 2 (Listen-B) Bemerkungen zur Auswertung = [] = f x : map f xs • Normale und verzögerte Reihenfolge der Auswertung reduzieren nicht die Argumenteausdrücke von Konstruktoren ⇒ Dadurch kann man (potentiell) unendliche Listen verarbeiten Beispiele: kann man auch so programmieren: (1+2):[] 1:repeat 1 map f lst = (case lst of [] -> []; (x:xs) -> f x : map f xs) Tests: Grundlagen der Programmierung 2 (Listen-B) – 62/64 – – 63/64 – Grundlagen der Programmierung 2 (Listen-B) wird nicht reduziert: ist ein Wert wird nicht reduziert: ist ein Wert seq (1:repeat 1) 7 seq (bot:repeat 1) 8 – 64/64 –