Vier Folien pro Seite

Werbung
Funktionale Programmierung
3.5.2005
Das Funktionale Quiz
Warum kann man keine Funktion schreiben, die die erste
Komponente eines beliebgen Tupels von Integern zurückgibt?
Weil Tupel unterschiedlicher Länge unterschiedliche Typen haben
Das Funktionale Quiz
Das Funktionale Quiz
Was ist das Problem mit folgender Definition für eine while-Schleife:
while :: Bool -> Int -> Int
while test-expr body-expr =
if test-expr then
body-expr; while test-expr body-expr
else 0
• ; ist kein Sequenzoperator, sondern beendet eine Definition
• test-expr hat stets den selben Wert (in der FP hat ein Ausdruck
genau einen Wert
Was ist der Wert von f 42
mystery = False || mystery
f x
| mystery
= x
| otherwise = 0
⊥
1-7
Themen heute
Universelle Polymorphie
• Idee: Abstrahiere über Typen
• Beispiel:
• Polymorphie
• Lokale Definitionen
fst :: (Int,Bool) -> Int
fst (x,y) = x
• Funktionen über Listen
• Beweise mit struktureller Induktion
fst :: (String,Float) -> String
fst (x,y) = x
• Wiederverwendung für verschiedene Typen möglich
Universelle Polymorphie
Allgemeinster Typ
• Haskell inferiert immer den allgemeinsten Typ
• Es gilt: Je mehr Typvariablen desto allgemeiner
• Allgemein: fst :: (a,b) -> a , ∀ a ∈ Typen
• Beispiel:
• a und b sind Typvariablen
(a,b) -> a ist allgemeiner als
• fst :: (Int,Bool) -> Int ist eine Instanz des
generischen Typs fst :: (a,b) -> a
(a,a) -> a
• Aber:
(a,b) -> c ist kein Typ für fst
Problem: Falsch abstrahiert
8-11
Unifikation
Typinferenz in groben Zügen
• Von innen nach außen
Versuche allgemeinsten Typ zweier Typen zu bestimmen
• Typ der Literale und Primitiva bekannt
• Äußerster Typ unterschiedlich: Fehlschlag
• Variabeln bekommen zunächst Typ-Variablen
• Basistypen gleich: Ok
• Unifizieren bei
• Typ und Typvariable: Instanziiere Typvariable
Fallunterscheidungen: Alle Fälle
• Gleiche zusammengesetzte Typen: Unifiziere Elemente
Funktionsaufrufen: Parameter(-Pattern) und Argumente
Funktionsaufrufen: Rückgabetyp und Aufruf
Die polymorphen Typen einiger Funktionen
Polymorphe Typsynonyme
• curry :: ( (a,b) -> c) -> a -> b -> c
• Auch Typsynonyme können Typvariablen enthalten
• (.) :: (a -> b) -> (b -> c) -> a -> c
• Syntax: type name tvar ... = Typ
• three :: a -> Int
• Beispiel: type Pair x y = (x,y)
• error :: String -> a
12-15
Listenfunktionen
(De-)Serialisierung
Sind zumeist polymorph - Abstraktion über den Typ der
Listenelemente
(:) :: a -> [a] -> [a]
vorne anhängen
(++) :: [a] -> [a] -> [a]
zusammenhängen
(!!) :: [a] -> Int -> a
Indexzugriff
concat :: [[a]] -> [a]
plätten
length :: [a] -> Int
Länge
take :: Int -> [a] -> [a]
ersten n Elemente
drop :: Int -> [a] -> [a]
ohne ersten n Elemente
reverse :: [a] -> [a]
umdrehen
• show konvertiert einen Wert in einen String
• read konvertiert einen String in den Wert, den er repräsentiert
show (True)
=> "True"
read "True" => True
zip :: [a] -> [b] -> [(a,b)] 2 Listen zu Liste aus Paaren
Lokale Definitionen
• Bis jetzt: Definitionen nur global
• Jetzt: lokale Definitionen mit where
Beispiel:
Syntax von Deklarationen
Decl-> var pat ...{| guard} = expr [ where Decls]
Decls-> { Decl1 ; ... ; DeclN }
Layout-Regel ersetzt ; und {}:
sumSquares :: Int -> Int -> Int
sumSquares n m = sqN + sqM
where
sqN = n * n
sqM = m * m
• Erstes Zeichen nach where ersetzt {
• Erstes Zeichen auf gleicher Höhe ersetzt ;
• Erstes Zeichen davor ersetzt }
16-19
Vorteile lokaler Definitionen
• Zwischenergebnisse mehrfach verwenden
• Lesbarer
• Pattern-Matching für Zwischenergebnisse
• Lokale Definitionen im gleichen Scope
Sichtbarkeitsbereich der lokalen Definitionen
Alternativ: let
Syntax: let Decls in expr
f x = let y = x * x
z = x * x * x in
(y,z)
Vorsicht!
Die Bindungen sind sichtbar:
• in allen rechten Seiten der Definitionen
• im Rumpf
Auswertung: Rumpf vereinfachen, dabei lokale Definitionen
einsetzen
dupAbs x = x + x
where
x = abs x
Terminiert nicht
20-23
Pattern-Matching
Konstruktoren für Listen
Bis jetzt bei Funktionsaufrufen verwendet
Mögliche Pattern:
• Literale
(:) : a -> [a] -> [a] ist Konstruktor für Liste:
• Variablen
[] ist eine Liste
• Wildcard _: matcht auf alles, bindet nichts
x:xs ist eine Liste
• Tupel von Pattern
• Konstruktor angewendet auf Pattern
Konstruktor: Baut Datentyp eindeutig auf
Pattern für Listen
Pattern-Matching-Ausdruck
(:) und [...] sind Pattern für Listen
head :: [a] -> a
head (x:_) = x
tail :: [a] -> [a]
tail (_:xs) = xs
sumThree :: [Int] -> Int
sumThree [x,y,z] = x + y +z
Bis jetzt: Pattern-Matching nur bei Funktionsdefinition
Als Ausdruck:
case exp of { alts }
alts
->
alt1 ; ... ; altn (n>=1)
alt
->
pat -> exp [where decls]
|
pat gdpat [where decls]
Haskell-Standard verwendet case im Kern
24-27
Primitive Rekursion über Listen
Primitive Rekursion über Listen
• Beispiele/Testfälle:
• Listen sind induktiv definiert
Ein Test für []
• Eine Funktion die Listen konsumiert, ist p.r. wenn sie beschrieben
werden kann durch:
Den Funktionswert für []
Eine Vorschrift, wie aus dem Funktionswert für xs, der
Funktionswert für x:xs berechnet wird
Mindestens ein Test für nicht-leere Listen
• Schablone:
f []
= ...
f (x:xs) = ... x ... (f xs) ... x ...
• Annehmen, dass f xs Funktionswert bereits berechnet
Insertion-Sort
Allgemeine Rekursion über Listen
iSort :: [Int] -> [Int]
iSort []
= []
iSort (x:xs) = ins x (iSort xs)
Um f (x:xs) zu berechnen, nimm an, dass f ys Funktion
berechnet,
ins
ins
ins
|
|
Schablone:
f []
= ...
f (x:xs) = ... (f ys) ...
:: Int ->
x []
x (y:ys)
x <= y
otherwise
[Int] -> [Int]
= [x]
= x:y:ys
= y : ins x ys
28-31
Quicksort
Testfälle für []
[] hat den Typ [a]
qSort :: [Int] -> [Int]
qSort
[] = []
qSort (x:xs) =
qSort [y | y <-xs, y <= x] ++
[x] ++
qSort [y <- xs, y>x]
Im Testfall f [] == [] kann die Überladung von == somit nicht
aufgelöst werden.
Auswege:
• Nur die leere Liste hat Länge 0: length (f []) == 0
• Typsignaturen für Ausdrücke:
f ([] :: [Int]) == ([] :: [Int])
Beweise mittels struktureller Induktion
Beispielbeweis
len []
= 0
len (x:xs) = 1 + len xs
z.Z.: len (xs++ys) = len xs + len ys
Prinzip der SI über Listen: Um eine Eigenschaft P(xs) für alle
endlichen Listen xs zu beweisen:
• Beweise Basisfall []
Beweis:
Sei ys beliebig aber fest,
• Beweise P(x:xs) mit der Annahme P(xs)
Da sich alle Listen xs aus [] und (:) konstruieren lassen, läßt sich so
für alle Listen xs ein Beweis für P(xs) konstruieren
len ([]++ys)
Def len
Def len
=
len ys = 0 + len ys
=
len xs + len ys
Assoz :
Def len
IA
=
len (x:xs++ys)
=
1 + len (xs++ys) =
Def len
1+len xs + len ys
=
len (x:xs) + len ys
len ((x:xs)++ys)
32-35
Herunterladen