Praktische Informatik 3: Funktionale Programmierung Vorlesung 3 vom 28.10.2014: Rekursive Datentypen
Christoph Lüth Universität Bremen Wintersemester 2014/15
Rev. 2746 1 [32]
Fahrplan
I Teil I: Funktionale Programmierung im Kleinen
IEinführung
IFunktionen und Datentypen
IRekursive Datentypen
ITypvariablen und Polymorphie
IFunktionen höherer Ordnung I
IFunktionen höherer Ordnung II
ITypinferenz
I Teil II: Funktionale Programmierung im Großen I Teil III: Funktionale Programmierung im richtigen Leben
2 [32]
Inhalt
I RekursiveDatentypen
I RekursiveDefinition
I . . . und wozu sie nützlich sind
I Rekursive Datentypen in anderen Sprachen
I Fallbeispiel: Labyrinthe
3 [32]
Der Allgemeine Fall: Algebraische Datentypen
Definition einesalgebraischen DatentypenT:
data T = C1t1,1. . .t1,k1
. . .
| Cntn,1. . .tn,kn
I KonstruktorenC1, . . . ,Cnsinddisjunkt:
Cix1. . .xn= Cjy1. . .ym=⇒i=j I Konstruktorensindinjektiv:
C x1. . .xn= C y1. . .yn=⇒xi= yi I Konstruktorenerzeugenden Datentyp:
∀x∈T.x= Ciy1. . .ym Diese Eigenschaften machenFallunterscheidungmöglich.
Heute:Rekursion
4 [32]
Rekursive Datentypen
I Der definierte TypTkannrechtsbenutzt werden.
I Rekursive Datentypen sindunendlich.
I Entsprichtinduktiver Definition
I ModelliertAggregation(Sammlung von Objekten)
I Funktionen werden durchRekursiondefiniert.
5 [32]
Algebraische Datentypen: Nomenklatur
Gegeben Definition dataT = C1t1,1. . .t1,k1
. . .
| Cntn,1. . .tn,kn I CisindKonstruktoren
IImmervordefiniert
I Selektorensind Funktionen seli,j: seli,j(Citi,1. . .ti,ki) = ti,j IPartiell, linksinvers zu Konstruktor
IKönnenvordefiniert werden (erweiterte Syntax derdataDeklaration) I Diskriminatorensind Funktionen disi:
disi :: T→Bool disi(Ci. . .) = True disi_ = False
IDefinitionsbereichsbereich des Selektors seli INievordefiniert
6 [32]
Uncle Bob’s Auld Time Grocery Shoppe Revisited
I Das Lager für Bob’s Shoppe:
I ist entweder leer,
I oder es enthält einen Artikel und Menge, und weiteres.
data Lager = LeeresLager
| Lager A r t i k e l Menge Lager
7 [32]
Suchen im Lager
I Rekursive Suche (erste Version):
suche :: A r t i k e l→ Lager→Menge suche art LeeresLager = ???
I Modellierung desResultats:
data Resultat = Gefunden Menge | NichtGefunden I Damit rekursiveSuche:
suche :: A r t i k e l→ Lager→ Resultat suche art (Lager l a r t m l )
| art == l a r t = Gefunden m
| otherwise = suche art l suche art LeeresLager = NichtGefunden
8 [32]
Einlagern
I Mengen sollen aggregiert werden, d.h. 35l Milch und 20l Milch werden zu 55l Milch.
I Dazu Hilfsfunktion:
addiere (Stueck i ) (Stueck j )= Stueck ( i+ j ) addiere (Gramm g) (Gramm h) = Gramm (g+ h) addiere ( L i t e r l ) ( L i t e r m) = L i t e r ( l+ m)
addiere m n = er ro r (" addiere : ␣"++ show m++ "␣und␣"++ show n) I Damit einlagern:
einlagern :: A r t i k e l→Menge→Lager→ Lager einlagern a m LeeresLager = Lager a m LeeresLager einlagern a m (Lager a l ml l )
| a == a l = Lager a ( addiere m ml) l
| otherwise = Lager a l ml ( einlagern a m l ) I Problem: Falsche Mengenangaben
I z.B.einlagern Eier ( Liter 3.0) l
9 [32]
Einlagern (verbessert)
I Eigentliche Funktioneinlagernwird alslokale Funktionversteckt, und nur mit gültiger Mengenangabe aufgerufen:
einlagern :: A r t i k e l→Menge→ Lager→ Lager einlagern a m l =
let einlagern ’ a m LeeresLager = Lager a m LeeresLager einlagern ’ a m (Lager a l ml l )
| a == a l = Lager a ( addiere m ml) l
| otherwise = Lager a l ml ( einlagern ’ a m l ) in case p r e i s a mof
Ungueltig → l _→ einlagern ’ a m l
10 [32]
Einkaufen und bezahlen
I Wir brauchen einenEinkausfwagen:
data Einkaufswagen = LeererWagen
| Einkauf A r t i k e l Menge Einkaufswagen I Artikel einkaufen:
einkauf :: A r t i k e l→Menge→Einkaufswagen→ Einkaufswagen einkauf a m e =
case p r e i s a mof Ungueltig → e _→ Einkauf a m e I Gesamtsumme berechnen:
kasse :: Einkaufswagen→ Int kasse LeererWagen = 0
kasse ( Einkauf a m e) = cent a m+ kasse e
11 [32]
Beispiel: Kassenbon
kassenbon :: Einkaufswagen→ String Ausgabe:
Bob’s Aulde Grocery Shoppe
Artikel Menge Preis
---
Schinken 50 g. 0.99 EU
Milch Bio 1.0 l. 1.19 EU
Schinken 50 g. 0.99 EU
Apfel Boskoop 3 St 1.65 EU
=====================================
Summe: 4.82 EU
Unveränderlicher Kopf
Ausgabe von Artikel und Mange (rekur- siv)
Ausgabe vonkasse
12 [32]
Kassenbon: Implementation
I Kernfunktion:
a r t i k e l :: Einkaufswagen→ String a r t i k e l LeererWagen = ""
a r t i k e l ( Einkauf a m e) = formatL 20 (show a) ++ formatR 7 (menge m) ++
formatR 10 (showEuro ( cent a m)) ++ "\n"++ a r t i k e l e
I Hilfsfunktionen:
formatL :: Int→ String→ String
13 [32]
Rekursive Typen in Java
I Nachbildung durch Klassen, z.B. für Listen:
class L i s t {
public L i s t (Object el , L i s t t l ) { this. elem= e l ;
this. next= t l ; }
public Object elem ; public L i s t next ; I Länge (iterativ):
int length () { int i= 0;
for ( L i s t cur=this; cur !=null; cur= cur . next ) i ++ ;
return i ; }
14 [32]
Rekursive Typen in C
I C: Produkte, Aufzählungen, keine rekursiven Typen I Rekursion durchZeiger
typedef struct l i s t _ t { void ∗elem ; struct l i s t _ t ∗next ; }∗l i s t ;
I Konstruktorennutzerimplementiert l i s t cons(void ∗hd , l i s t t l ) { l i s t l ;
i f (( l= ( l i s t ) malloc (sizeof(struct l i s t _ t )))== NULL) { p r i n t f ( "Out␣of␣memory\n" ) ; e x i t (−1);
}
l→elem= hd ; l→ next= t l ; return l ;
}
15 [32]
Fallbeispiel: Zyklische Datenstrukturen
Quelle: docs.gimp.org
16 [32]
Modellierung eines Labyrinths
I EingerichtetesLabyrinth ist entweder
I eine Sackgasse,
I ein Weg, oder
I eine Abzweigung in zwei Richtungen.
dataLab = Dead Id
| Pass Id Lab
| TJnc Id Lab Lab
I Ferner benötigt: eindeutigeBezeichnerder Knoten type Id = Integer
17 [32]
Ein Labyrinth (zyklenfrei)
0 1 2 3 4
5?
6 7?
8 6
-9 6
10?
-11 12 -13 6
14?
15 16
? -17
6
18 -19
20 6
21 22? -23
6 24?
18 [32]
Traversion des Labyrinths
I Ziel:Pfadzu einem gegebenZielfinden
I BenötigtPfadeundTraversion data Path = Cons Id Path
| Mt data Trav = Succ Path
| F a i l
19 [32]
Traversionsstrategie
I Geht vonzyklenfreienLabyrinth aus
I An jedem Knoten prüfen, ob Ziel erreicht, ansonsten
Ian SackgasseFail
Ian Passagen weiterlaufen
Ian Kreuzungen Auswahl treffen I Erfordert Propagation vonFail:
cons :: Id→ Trav→Trav s e l e c t :: Trav→ Trav→Trav
20 [32]
Zyklenfreie Traversion
traverse1 :: Id→ Lab→ Trav traverse1 t l
| nid l == t = Succ (Cons ( nid l ) Mt)
| otherwise =case l of Dead _→ F a i l
Pass i n→ cons i ( traverse1 t n)
TJnc i n m→ s e l e c t (cons i ( traverse1 t n)) (cons i ( traverse1 t m))
21 [32]
Traversion mit Zyklen
I VeränderteStrategie: Pfad bis hierher übergeben
IPfad musshintenerweitert werden.
I WennaktuellerKnoten in bisherigen Pfadenthaltenist,Fail I Ansonsten wie oben
I Neue Hilfsfunktionen:
contains :: Id→ Path→Bool cat :: Path→Path→ Path snoc :: Path→ Id→Path
22 [32]
Traversion mit Zyklen
traverse2 :: Id→ Lab→ Path→Trav traverse2 t l p
| nid l == t = Succ (snoc p ( nid l ))
| contains ( nid l ) p = F a i l
| otherwise =case l of Dead _→ F a i l
Pass i n→ traverse2 t n (snoc p i )
TJnc i n m→ s e l e c t ( traverse2 t n (snoc p i )) ( traverse2 t m (snoc p i ))
23 [32]
Ein Labyrinth (mit Zyklen)
0 1 2 3 4
5?
∗ 6 7? 8
6 -9
6
10?
-11 12 -13 6
14?
15
∗6 16?
-17 6
18 -19
20 6
21 22
? -23
6 24
?
24 [32]
Ungerichtete Labyrinth
I In einemungerichtetenLabyrinth haben Passagen keine Richtung.
I Sackgassen haben einen Nachbarn,
I eine Passage hat zwei Nachbarn,
I und eine Abzweigung drei Nachbarn.
dataLab = Dead Id Lab
| Pass Id Lab Lab
| TJnc Id Lab Lab Lab
I Andere Datentypen und Hilfsfunktionen bleiben (mutatis mutandis) I Jedes nicht-leere ungerichtete Labyrinth hatZyklen.
I Invariante(nicht durch Typ garantiert)
25 [32]
Traversion in ungerichteten Labyrinthen
I Traversionsfunktion wie vorher traverse3 :: Id→Lab→Path→ Trav traverse3 t l p
| nid l == t = Succ (snoc p ( nid l ))
| contains ( nid l ) p = F a i l
| otherwise =case l of
Dead i n → traverse3 t n (snoc p i )
Pass i n m→ s e l e c t ( traverse3 t n (snoc p i )) ( traverse3 t m (snoc p i )) TJnc i n m k → s e l e c t ( traverse3 t n (snoc p i ))
( s e l e c t ( traverse3 t m (snoc p i )) ( traverse3 t k (snoc p i )))
26 [32]
Zusammenfassung Labyrinth
I Labyrinth−→GraphoderBaum I In Haskell: gleicher Datentyp I Referenzen nichtexplizitin Haskell
I KeineundefiniertenReferenzen (erhöhteProgrammsicherheit)
I KeineGleichheitauf Referenzen
I Gleichheit istimmerstrukturell (oderselbstdefiniert)
27 [32]
Beispiel: Zeichenketten selbstgemacht
I EineZeichenketteist
Ientwederleer(das leere Wort)
Ioder einZeichencund eine weitereZeichenkettexs dataMyString = Empty
| Cons Char MyString
I LineareRekursion
IGenau ein rekursiver Aufruf
28 [32]
Rekursive Definition
I Typisches Muster:Fallunterscheidung
I EinFallproKonstruktor
I Hier:
I LeereZeichenkette
I NichtleereZeichenkette
29 [32]
Funktionen auf Zeichenketten
I Länge:
len :: MyString→ Int
len Empty = 0
len (Cons c s t r ) = 1+ len s t r I Verkettung:
cat :: MyString→MyString→ MyString cat Empty t = t
cat (Cons c s ) t = Cons c ( cat s t ) I Umkehrung:
rev :: MyString→MyString rev Empty = Empty
rev (Cons c t ) = cat ( rev t ) (Cons c Empty)
30 [32]
Was haben wir gesehen?
I StrukturellähnlicheTypen:
I Einkaufswagen,Path,MyString(Listen-ähnlich)
I Resultat,Preis,Trav(Punktierte Typen) I ÄhnlicheFunktionendarauf
I Besser:eineTypdefinition mit Funktionen, instantiierung zu verschiedenen Typen
Nächste Vorlesung
31 [32]
Zusammenfassung
I Datentypen könnenrekursivsein
I Rekursive Datentypen sindunendlich(induktiv) I Funktionen werdenrekursivdefiniert
I Fallbeispiele: Einkaufen in Bob’s Shoppe, Labyrinthtraversion I Viele strukturell ähnliche Typen
I NächsteWoche: Abstraktion über Typen (Polymorphie)
32 [32]