Grundlagen der Programmierung 2 A (Listen) [1.5ex] Haskell: Listen

Werbung
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 2014
[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.
[] und (x:xs) links von =“
”
Z.B. Muster
und Argument
ergibt die Ersetzung:
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 : [])
sind Muster(Pattern)
(x:xs)
(s:t)
[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,. . .
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 (bzw. Verwendung / Intention) 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 = s in t
f x y z=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
die freien Variablen x, y, z in t werden gebunden.
t ist der lexikalische Gültigkeitsbereich von
x, y, z.
s.o.
Grundlagen der Programmierung 2 (Listen-B)
– 41/64 –
Beispiele
Ausdruck t = \x -> (x (\x -> x*x))
x ist in t gebunden, aber in zwei Bindungsbereichen:
\x -> (x (\x -> x*x))
In (x (\x -> x*x)) kommt x frei und gebunden vor.
Umbenennen des gebundenen x in y ergibt:
(x (\y -> y*y))
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
Auswertungsregel 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 Auswertungsregel)
(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
werten nicht die Argumentausdrücke von Konstruktoren aus
⇒
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 ausgewertet: ist ein Wert
wird nicht ausgewertet: ist ein Wert
seq (1:repeat 1) 7
seq (bot:repeat 1) 8
– 64/64 –
Zugehörige Unterlagen
Herunterladen