Programmieren in Haskell

Werbung
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Programmieren in Haskell
WS 2011/2012
Strictness
Control
More on fold
Strukturelle
Rekursion
Robert Giegerich1
Universität Bielefeld
AG Praktische Informatik
11. Dezember 2013
1
[email protected]
Vorschau
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Strictness
Control
Themen heute:
Funktionen höherer Ordnung (Fortsetzung)
künstliche Striktheit
mehr zu fold
Strukturelle Rekursion für alle (rekursiven) Datentypen
More on fold
Strukturelle
Rekursion
Funktionen höherer Ordnung
1
Zum Beispiel die Funktions-Komposition:
infixr 9 .
2
3
4
5
6
1
(.)
:: ( b -> c ) -> ( a -> b ) -> a -> c
f . g
= \ x -> f ( g x )
Oder:
(.)
:: ( b -> c ) -> ( a -> b ) -> a -> c
(.) f g x = f ( g x )
Übersichtlich für Algorithmen mit mehreren Phasen
> treeSort = sortTree . build
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Strictness
Control
More on fold
Strukturelle
Rekursion
2
3
4
> compile = writeCode .m - opt
. codegen . implmap .
>
transform . semCheck . parse . tokenize
Das zweite Beispiel beschreibt die typischen Phasen eines
Compilers
$, $! und seq
Universität Bielefeld
Programmieren
in Haskell
1
Oder sogar die Funktionsanwendung als Funktion:
infixr 0 $
2
3
4
($)
f $
x
:: ( a -> b ) -> a -> b
= f x -- $ heisst " apply "
Giegerich
Strictness
Control
More on fold
Strukturelle
Rekursion
infix application operator
spart Klammern
1
2
Strikte Funktionsanwendung
> infixr 0 $ !
> f $ ! x = x ‘seq ‘ f x -- = f x , aber strikt
seq ist die sequentielle Auswertung und dient der Vermeidung
unerwünschter Laziness.
$, $! und seq (2)
Universität Bielefeld
Programmieren
in Haskell
seq ist eingebaute Funktion mit den Eigenschaften
1
2
Giegerich
a ‘seq‘ b = ⊥, falls a = ⊥
(1)
a ‘seq‘ b = b, falls x 6= ⊥
(2)
Es ändert ggf. Laufzeit (schneller oder langsamer),
Speicherplatz (i.A. weniger) und Terminierungsverhalten (falls
überhaupt problematisch), nicht aber die Ergebnisse einer
Funktion wenn sie definiert ist. Vergleiche:
let { cond a b c = if a then b else c ; x = x }
in ( cond True 1) $ x
3
4
5
let { cond a b c = if a then b else c ; x = x }
in ( cond True 1) $ ! x
Strictness
Control
More on fold
Strukturelle
Rekursion
foldr1
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Strictness
Control
1
2
3
4
Listerverarbeitung ohne Startwert, von rechts:
foldr1 :: ( a -> a -> a ) -> [ a ] -> a
foldr1 (*) = f
where f [ a ] = a
f ( a : b : xs ) = a * f ( b : xs )
Das letzte Listenelement dient als Startwert
More on fold
Strukturelle
Rekursion
foldl1
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Strictness
Control
More on fold
1
2
... und das Gleiche von links her:
foldl1 :: ( a -> a -> a ) -> [ a ] -> a
foldl1 (*) ( x : xs ) = foldl (*) x xs
Das erste Listenelement dient als Startwert
Strukturelle
Rekursion
foldl1-Beispiele
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Strictness
Control
1
2
minimum = foldl1 min
maximum = foldl1 max
Geht’s kürzer?
More on fold
Strukturelle
Rekursion
Wiederholung: Strukturelle Rekursion
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Schema:
Strictness
Control
f
:: [σ] -> τ
f []
=
e1
f (a : as)
=
e2
where s
=
f as
wobei e1 und e2 Ausdrücke vom Typ τ sind und e2 die
Variablen a, as und s (nicht aber f ) enthalten darf.
More on fold
Strukturelle
Rekursion
Strukturelle Rekursion
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Strictness
Control
Viele Algorithmen auf Listen folgen dem Schema der
strukturellen Rekursion
fold- Funktionen unterstützen dies als
Higher-Order-Functions
Was ist z.B. mit insert?
More on fold
Strukturelle
Rekursion
Beipiel: insert
Universität Bielefeld
Programmieren
in Haskell
1
2
3
4
5
6
7
8
insert hat 2 Argumente – formell keine strukturelle Rekursion
Aber: insert a folgt dem Schema
> insert x [] = [ x ]
> insert x ( y : ys )
>
| x <= y = x : y : ys
>
| x > y = y :( insert x ys )
Explizit:
> ins []
= \ a -> [ a ]
> ins (a ’: as ) = \ a -> if a <= a ’
>
then a :a ’: as
>
else a ’:( ins as ) a
⇒ Unnatürlich?
Giegerich
Strictness
Control
More on fold
Strukturelle
Rekursion
Erweitertes Rekursionsschema
Universität Bielefeld
Programmieren
in Haskell
Giegerich
g ::
σ1 → [σ2 ] → τ
g i [] =
e1
g i (a : as) =
e2
where s =
g e3 as
Ausdruck
Variablen
e1
e2
e3
enthält i
kann i, a, as und s enthalten
kann i, a und as enthalten
Strictness
Control
More on fold
Strukturelle
Rekursion
Strukturelle Rekursion auf Bäumen
Universität Bielefeld
Programmieren
in Haskell
weitere Datenstruktur: Bäume
Giegerich
Modellierung in Haskell
Strictness
Control
→ als algebraischer Datentyp
More on fold
Example
Strukturelle
Rekursion
·
·
·
1 2
·
3 4
5
Eine von vielen Baum-Varianten
Universität Bielefeld
Programmieren
in Haskell
Welche Eigenschaften sollen unsere Bäume haben?
an den Blättern stehen Daten
jeder innere Knoten hat 2 Kinder
Darstellung:
Bäume werden in der Informatik von oben nach unten
gezeichnet
Begriffe:
Wurzel
Knoten (Blatt, innerer Knoten)
Kante
Tiefe
Giegerich
Strictness
Control
More on fold
Strukturelle
Rekursion
Tree Datentyp
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Strictness
Control
Tree-Datenyp
1
2
3
4
data Tree a = Leaf a
|
Br
( Tree a ) ( Tree a ) |
Nil
deriving Show
More on fold
Strukturelle
Rekursion
Beispiel
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Br
Strictness
Control
Br
Br
Br
Leaf
Leaf Leaf
5
Leaf Leaf
1
2
3
1
2
Br
More on fold
4
( Br ( Leaf 1) ( Leaf 2) )
( Br ( Br ( Leaf 3) ( Leaf 4) ( Leaf 5)))
Strukturelle
Rekursion
Strukturelle Rekursion auf Bäumen
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Schema der strukturellen Rekursion:
f
:: Tree σ -> τ
f Nil
=
e1
f (Leaf a)
=
e2
f (Br l r)
=
e3
where sl
=
f l
sr
=
f r
e3 darf dabei l, r, sl und sr enthalten, nicht aber f .
Strictness
Control
More on fold
Strukturelle
Rekursion
Strukturelle Rekursion auf Bäumen
1
2
3
4
data Tree a = Leaf a
|
Br
( Tree a ) ( Tree a ) |
Nil
deriving Show
Rekursionsbasis (Nil) Das Problem wird für den leeren
Baum gelöst.
Rekursionsbasis (Leaf a) Das Problem wird für das Blatt
Leaf a gelöst.
Rekursionsschritt (Br l r) Um das Problem für den Baum
Br l r zu lösen, werden rekursiv Lösungen für l
und r bestimmt, die zu einer Lösung für Br l r
erweitert werden.
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Strictness
Control
More on fold
Strukturelle
Rekursion
Beispiel: Berechnung der Baumgröße
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Strictness
Control
More on fold
1
2
3
4
size
size
size
size
:: Tree a -> Integer
Nil
= 1
( Leaf _ ) = 1
( Br l r ) = size l + size r
Strukturelle
Rekursion
Beispiel: Berechnung der Baumtiefe
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Strictness
Control
More on fold
1
2
3
4
depth
depth
depth
depth
:: Tree a -> Integer
Strukturelle
Rekursion
Nil
= 0
( Leaf _ ) = 0
( Br l r ) = max ( depth l ) ( depth r ) + 1
Beispiel: Blätter aufzählen
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Strictness
Control
1
2
3
4
>
>
>
>
leaves
leaves
leaves
leaves
:: Tree a -> [ a ]
Nil
= []
( Leaf a ) = [ a ]
( Br l r ) = leaves l ++ leaves r
⇒ Geht es nicht besser?
More on fold
Strukturelle
Rekursion
Verstärkung der Rekursion (Einbettung)
Universität Bielefeld
Programmieren
in Haskell
Exkurs zu leaves
1
2
3
4
5
6
7
Giegerich
Verstärkung der Rekursion
Strictness
Control
wie bei der rekursiven Listenfunktion reverse
More on fold
fleaves
:: Tree a -> [ a ]
fleaves t = f t []
where
f
:: Tree a -> [ a ] -> [ a ]
f Nil
y = y
f ( Leaf a ) y = a : y
f ( Br l r ) y = f l ( f r y )
Und warum ist das nun schneller?
Strukturelle
Rekursion
Apropos reverse
Universität Bielefeld
Programmieren
in Haskell
Giegerich
1
2
1
2
3
Die langsame Version:
> slowreverse [] = []
> slowreverse ( x : xs ) = slowreverse xs ++ [ x ]
Schneller durch Einbettung
> fastreverse xs = f xs [] where
>
f [] ys = ys
>
f ( x : xs ) ys = f xs ( x : ys )
Strictness
Control
More on fold
Strukturelle
Rekursion
fold auf Bäumen
Universität Bielefeld
Programmieren
1
2
3
4
5
1
2
3
in Haskell
Die Funktionen size, depth, leaves folgen alle dem
Giegerich
gleichen Schema.
> foldTree :: b - >(a - > b ) - >(b - >b - > b ) - > Tree a -> Strictness
b
Control
> foldTree nil leaf br = f where
More on fold
>
f Nil = nil
Strukturelle
>
f ( Leaf a ) = leaf a
Rekursion
>
f ( Br l r ) = br ( f l ) ( f r )
Damit erhalten wir
> size ’ = foldTree 1 (\ x - >1)
(+)
> depth ’ = foldTree 0 (\ x - >0) (\ x y - > max x y +1)
> leaves ’= foldTree [] (\ x - >[ x ]) (++)
Das findet man einfach durch Anwendung der
Vogelperspektive
fold im Allgemeinen
Verallgemeinerung:
für jeden algebraischen Datentyp T können wir eine
foldT-Funktion definieren
für jeden Konstruktor hat sie eine passende Funktion als
Argument, sowie ein t ∈ T
in der Vogelperspektive werden die Konstruktoren durch
die passenden Funktionen ersetzt (und anschließend die
entstandene Formel ausgerechnet).
Je komplizierter der Datentyp, deso mehr gewinnt man durch
die Verwendung der fold-Operation
1
PS: Und was tut die folgende Funktion f?
> f = foldTree Nil Leaf Br
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Strictness
Control
More on fold
Strukturelle
Rekursion
Das allgemeine Rekursionsschema
Universität Bielefeld
Programmieren
in Haskell
Giegerich
data T a1 . . . am = C1 t11 . . . t1n1
| ...
| Cr tr 1 . . . trnr
Wir unterscheiden zwei Arten von Argumenten: rekursive (d.h.
tij ist gleich T a1 . . . am ) und nicht-rekursive.
Seien li1 ,. . . , lipi mit 1 ≤ li1 < li2 < · · · < lipi ≤ ni die
Positionen, an denen der Konstruktor Ci rekursiv ist
Strictness
Control
More on fold
Strukturelle
Rekursion
Das allgemeine Schema der strukturellen Rekursion
f
:: T σ1 . . . σm -> τ
f (C1 x11 . . . x1n1 )
=
e1
where s11
=
f x1l11
=
f x1l1p1
f (Cr xr 1 . . . xrnr )
=
er
where sr 1
=
f xrlr 1
=
f xrlrpr
...
...
srpr
Programmieren
in Haskell
Giegerich
Strictness
Control
More on fold
...
s1p1
Universität Bielefeld
Der Ausdruck ei darf die Variablen xi1 , . . . , xini und die
Variablen si1 , . . . , sipi enthalten. Ist pi = 0, so spricht man von
einer Rekursionsbasis, sonst von einem Rekursionsschritt.
Strukturelle
Rekursion
Fazit
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Strictness
Control
Strukturelle Rekursion für jeden algebraischen Datentyp
direkte Orientierung an den Konstruktoren
lassen sich alle Probleme mit struktureller Rekursion lösen?
strukturelle versus wohlfundierte Rekursion
wie war das mit der Implementierung der
Registermaschine?
More on fold
Strukturelle
Rekursion
Herunterladen