Praktische Informatik 3: Funktionale Programmierung Vorlesung 3 vom 01.11.2016: Algebraische Datentypen
Christoph Lüth Universität Bremen Wintersemester 2016/17
16:02:22 2017-01-17 1 [35]
Fahrplan
I Teil I: Funktionale Programmierung im Kleinen
IEinführung
IFunktionen und Datentypen
I Algebraische Datentypen
ITypvariablen und Polymorphie
IFunktionen höherer Ordnung I
IFunktionen höherer Ordnung II und Effizenzaspekte I Teil II: Funktionale Programmierung im Großen I Teil III: Funktionale Programmierung im richtigen Leben
PI3 WS 16/17 2 [35]
Inhalt
I RekursiveDatentypen
I RekursiveDefinition
I . . . und wozu sie nützlich sind
I Rekursive Datentypen in anderen Sprachen
I Fallbeispiel: Labyrinth
PI3 WS 16/17 3 [35]
Algebraische Datentypen
data T = C1t1,1 . . .t1,k1
| C2t2,1 . . .t2,k2
...
| Cntn,1 . . .tn,kn
I Aufzählungen
I Konstrukturen miteinemodermehrerenArgumenten (Produkte) I Der allgemeine Fall:mehrereKonstrukturen
Heute:Rekursion
PI3 WS 16/17 4 [35]
Der Allgemeine Fall: Algebraische Datentypen
data T = C1t1,1. . .t1,k1
| C2t2,1. . .t2,k2
...
| Cntn,1. . .tn,kn
Drei Eigenschaften eines algebraischen Datentypen 1.KonstruktorenC1, . . . ,Cnsinddisjunkt:
Cix1. . .xn= Cjy1. . .ym=⇒i=j 2.Konstruktorensindinjektiv:
C x1. . .xn= C y1. . .yn=⇒xi= yi 3.Konstruktorenerzeugenden Datentyp:
∀x∈T.x= Ciy1. . .ym
Diese Eigenschaften machenFallunterscheidungwohldefiniert.
PI3 WS 16/17 5 [35]
Algebraische Datentypen: Nomenklatur
data T = C1t1,1. . .t1,k1
...
| Cntn,1. . .tn,kn I CisindKonstruktoren
IImmervordefiniert
I Selektorensind Funktionen seli,j: seli,j :: T→ti,ki
seli,j(Citi,1. . .ti,ki) = ti,j ILinksinvers zu Konstruktor Ci, partiell
IKönnenvordefiniert werden (erweiterte Syntax derdataDeklaration) I Diskriminatorensind Funktionen disi:
disi :: T→Bool disi(Ci. . .) = True disi_ = False
IDefinitionsbereichsbereich des Selektors seli,nievordefiniert
PI3 WS 16/17 6 [35]
Rekursive Datentypen
I Der definierte TypTkannrechtsbenutzt werden.
I Rekursive Datentypen definierenunendlich großeWertemengen.
I ModelliertAggregation(Sammlung von Objekten).
I Funktionen werden durchRekursiondefiniert.
Uncle Bob’s Auld Time Grocery Shoppe Revisited
I Das Lager für Bob’s Shoppe:
Iist entweder leer,
Ioder es enthält einen Artikel und Menge, und weiteres.
dataLager = LeeresLager
| Lager A r t i k e l Menge Lager
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
PI3 WS 16/17 9 [35]
Einlagern
I Mengen sollen aggregiert werden (35l Milch + 20l Milch = 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
Iz.B.einlagern Eier ( L i t e r 3.0) l
PI3 WS 16/17 10 [35]
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
PI3 WS 16/17 11 [35]
Einkaufen und bezahlen
I Wir brauchen einenEinkausfwagen:
dataEinkaufswagen = 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
PI3 WS 16/17 12 [35]
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
PI3 WS 16/17 13 [35]
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
PI3 WS 16/17 14 [35]
Rekursive Typen in imperativen Sprachen
PI3 WS 16/17 15 [35]
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 ; }
PI3 WS 16/17 16 [35]
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 ;
}
PI3 WS 16/17 17 [35]
Fallbeispiel
PI3 WS 16/17 18 [35]
Fallbeispiel: Zyklische Datenstrukturen
Quelle: docs.gimp.org
PI3 WS 16/17 19 [35]
Modellierung eines Labyrinths
I EingerichtetesLabyrinth ist entweder
Ieine Sackgasse,
Iein Weg, oder
Ieine Abzweigung in zwei Richtungen.
dataLab = Dead Id
| Pass Id Lab
| TJnc Id Lab Lab
I Ferner benötigt: eindeutigeBezeichnerder Knoten type Id = Integer
PI3 WS 16/17 20 [35]
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?
PI3 WS 16/17 21 [35]
Traversion des Labyrinths
I Ziel:Pfadzu einem gegebenZielfinden
I BenötigtPfadeundTraversion dataPath = Cons Id Path
| Mt dataTrav = Succ Path
| F a i l
PI3 WS 16/17 22 [35]
Traversionsstrategie
I Geht vonzyklenfreienLabyrinth aus
I An jedem Knoten prüfen, ob Ziel erreicht, ansonsten
I an SackgasseF a i l
I an Passagen weiterlaufen
I an Kreuzungen Auswahl treffen I Erfordert Propagation vonF a i l:
cons :: Id→Trav→ Trav s e l e c t :: Trav→Trav→ Trav
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)) I Wie mit Zyklen umgehen?
I An jedem Knoten prüfen ob schon im Pfad enthalten
Traversion mit Zyklen
I VeränderteStrategie: Pfad bis hierher übergeben
I Pfad musshintenerweitert werden.
I WennaktuellerKnoten in bisherigen Pfadenthaltenist,F a i l I Ansonsten wie oben
I Neue Hilfsfunktionen:
contains :: Id→Path→ Bool snoc :: Path→ Id→ Path
PI3 WS 16/17 25 [35]
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 ))
PI3 WS 16/17 26 [35]
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?
PI3 WS 16/17 27 [35]
Ungerichtete Labyrinth
I In einemungerichtetenLabyrinth haben Passagen keine Richtung.
ISackgassen haben einen Nachbarn,
Ieine Passage hat zwei Nachbarn,
Iund 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)
PI3 WS 16/17 28 [35]
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 )))
PI3 WS 16/17 29 [35]
Zusammenfassung Labyrinth
I Labyrinth−→GraphoderBaum I In Haskell: gleicher Datentyp I Referenzen nichtexplizitin Haskell
IKeineundefiniertenReferenzen (erhöhteProgrammsicherheit)
IKeineGleichheitauf Referenzen
IGleichheit istimmerstrukturell (oderselbstdefiniert)
PI3 WS 16/17 30 [35]
Beispiel: Zeichenketten selbstgemacht
I EineZeichenketteist
I entwederleer(das leere Wort)
I oder einZeichencund eine weitereZeichenkettexs data MyString = Empty
| Cons Char MyString
I LineareRekursion
I Genau ein rekursiver Aufruf
PI3 WS 16/17 31 [35]
Rekursive Definition
I Typisches Muster:Fallunterscheidung
IEinFallproKonstruktor
I Hier:
ILeereZeichenkette
INichtleereZeichenkette
PI3 WS 16/17 32 [35]
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)
PI3 WS 16/17 33 [35]
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
PI3 WS 16/17 34 [35]
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)
PI3 WS 16/17 35 [35]