Fahrplan I Praktische Informatik 3: Einführung in die Funktionale Programmierung Vorlesung vom 10.11.2010: Rekursive Datentypen Christoph Lüth & Dennis Walter Teil I: Funktionale Programmierung im Kleinen I Einführung I Funktionen und Datentypen I Rekursive Datentypen I Typvariablen und Polymorphie I Funktionen höherer Ordnung I Typinferenz Universität Bremen Wintersemester 2010/11 Rev. 1152 I Teil II: Funktionale Programmierung im Großen I Teil III: Funktionale Programmierung im richtigen Leben 2 [23] 1 [23] Inhalt I I Der Allgemeine Fall: Algebraische Datentypen Definition eines algebraischen Datentypen T: data T = C1 t1,1 . . . t1,k1 ... | Cn tn,1 . . . tn,kn Rekursive Datentypen I Formen der Rekursion I Rekursive Definition I Rekursive Datentypen in anderen Sprachen I Konstruktoren C1 , . . . , Cn sind disjunkt: Ci x1 . . . xn = Cj y1 . . . ym −→ i = j I Konstruktoren sind injektiv: C x1 . . . xn = C y1 . . . yn −→ xi = yi I Konstruktoren erzeugen den Datentyp: ∀x ∈ T . x = Ci y1 . . . ym Induktiver Beweis I Schluss vom kleineren aufs größere Diese Eigenschaften machen Fallunterscheidung möglich. 3 [23] Rekursive Datentypen 4 [23] Induktive Definitionen I I Der definierte Typ T kann rechts benutzt werden. I Rekursive Datentypen sind unendlich I Entspricht induktiver Definition I Beispiel natürliche Zahlen: Peano-Axiome I 0∈N I wenn n ∈ N, dann S n ∈ N I S injektiv und S n 6= 0 I Induktionsprinzip: φ(0), φ(x ) −→ φ(S x ), dann ∀n ∈ N.φ(n) Induktionsprinzip erlaubt Definition rekursiver Funktionen: n+0 = n n + S m = S(n + m) 5 [23] Natürliche Zahlen in Haskell 6 [23] Beispiel: Zeichenketten selbstgemacht I Direkte Übersetzung der Peano-Axiome I Der Datentyp: I entweder leer (das leere Wort ) data Nat = Z er o | S Nat I oder ein Zeichen c und eine weitere Zeichenkette xs I I Eine Zeichenkette ist data M y S t r i n g = Empty | Cons Char M y S t r i n g Rekursive Funktionsdefinition: add :: Nat → Nat → Nat add n Z e ro = n add n ( S m) = S ( add n m) I Lineare Rekursion I 7 [23] Genau ein rekursiver Aufruf 8 [23] Rekursive Definition Funktionen auf Zeichenketten I I l e n :: M y S t r i n g → I n t l e n Empty = 0 l e n ( Cons c s t r ) = 1+ l e n s t r Typisches Muster: Fallunterscheidung I Ein Fall pro Konstruktor I I Länge: c a t :: M y S t r i n g → M y S t r i n g → M y S t r i n g c a t Empty t = t c a t ( Cons c s ) t = Cons c ( c a t s t ) Hier: I Leere Zeichenkette I Nichtleere Zeichenkette Verkettung: I Umkehrung: r e v :: M y S t r i n g → M y S t r i n g r e v Empty = Empty r e v ( Cons c t ) = c a t ( r e v t ) ( Cons c Empty ) 9 [23] Baumartige Rekursion: Binäre Bäume I Wechselseitige Rekursion: Variadische Bäume Datentyp: data BTree = MtBTree | BNode I n t BTree BTree I 10 [23] I data VTree = MtVTree | VNode S t r i n g VNodes Funktion, bsp. Höhe: data VNodes = MtVNodes | VMore VTree VNodes h e i g h t :: BTree → I n t h e i g h t MtBTree = 0 h e i g h t ( BNode j l r ) = max ( h e i g h t l ) ( h e i g h t r )+ 1 I Variable Anzahl Kinderknoten I Baumartige Rekursion I VNodes: Liste von Kinderbäumen Doppelter rekursiver Aufruf 11 [23] Wechselseitige Rekursion: Variadische Bäume I I 12 [23] Rekursive Typen in anderen Sprachen Hauptfunktion: I Standard ML: gleich c o u n t :: VTree → I n t c o u n t MtVTree = 0 c o u n t ( VNode _ n s ) = 1+ c o u n t _ n o d e s n s I Lisp: keine Typen, aber alles ist eine S-Expression Hilfsfunktion: I data SExpr = Quote Atom | Cons SExpr SExpr c o u n t _ n o d e s :: VNodes → I n t c o u n t _ n o d e s MtVNodes = 0 c o u n t _ n o d e s ( VMore t ns )= c o u n t t+ c o u n t _ n o d e s n s Python, Ruby: I Listen (Sequenzen) vordefiniert I Keine anderen Typen 13 [23] Rekursive Typen in Java I I 14 [23] Rekursive Typen in C Nachbildung durch Klassen, z.B. für Listen: I class List { public L i s t ( Object el , L i s t t l ) { t h i s . elem= e l ; t h i s . n e x t= t l ; } p u b l i c O b j e c t elem ; public L i s t next ; I C: Produkte, Aufzählungen, keine rekursiven Typen Rekursion durch Zeiger typedef s t r u c t l i s t _ t { void ∗ elem ; struct l i s t _ t ∗ next ; } ∗list ; I Länge (iterativ): int length () { i n t i= 0 ; f o r ( L i s t c u r= t h i s ; c u r 6= n u l l ; c u r= c u r . n e x t ) i ++ ; return i ; } 15 [23] Konstruktoren nutzerimplementiert l i s t c o n s ( v o i d ∗hd , l i s t t l ) { list l ; i f ( ( l= ( l i s t ) m a l l o c ( s i z e o f ( s t r u c t l i s t _ t ) ) )== NULL) { p r i n t f ( " Out o f memory \n " ) ; e x i t ( −1); } l → elem= hd ; l → n e x t= t l ; return l ; } 16 [23] Rekursive Definition, induktiver Beweis I Beweis durch vollständige Induktion Definition durch Rekursion I Basisfall (leere Zeichenkette) I Rekursion (nicht-leere Zeichenkette) Zu zeigen: Für alle natürlichen Zahlen x gilt P(x ). Beweis: r e v :: M y S t r i n g → M y S t r i n g r e v Empty = Empty r e v ( Cons c t ) = c a t ( r e v t ) ( Cons c Empty ) I I I Induktionsbasis: P(0) I Induktionsschritt: Reduktion der Eingabe (vom größeren aufs kleinere) Beweis durch Induktion I I Induktionsvoraussetzung P(x ) I zu zeigen P(x + 1) Schluß vom kleineren aufs größere 17 [23] Beweis durch strukturelle Induktion (Zeichenketten) 18 [23] Beweis durch strukturelle Induktion (Allgemein) Zu zeigen: Zu zeigen: Für alle (endlichen) Zeichenketten xs gilt P(xs) Für alle x in T gilt P(x ) Beweis: Beweis: I Induktionsbasis: P() I Induktionsschritt: I I Induktionsvoraussetzung P(xs) I zu zeigen P(x xs) Für jeden Konstruktor Ci : I Voraussetzung: für alle ti,j gilt P(ti,j ) I zu zeigen P(Ci ti,1 . . . ti,ki ) 19 [23] Beispielbeweise 20 [23] Spezifikation und Korrektheit I Die ersten n Zeichen einer Zeichenkette (n ≥ 0) takeN :: I n t → M y S t r i n g → M y S t r i n g takeN n Empty = Empty takeN n ( Cons c s ) = i f n == 0 then Empty e l s e Cons c ( takeN ( n−1) s ) len s ≥ 0 len (cat s t) = len s + len t len (rev s) = len s (1) I Zeichenkette ohne die ersten n Zeichen (n ≥ 0) dropN :: I n t → M y S t r i n g → M y S t r i n g dropN n Empty = Empty dropN n ( Cons c s ) = i f n == 0 then Cons c s e l s e dropN ( n− 1 ) s (2) (3) I Eigenschaften: len (takeN n s) ≤ n (4) len (dropN n s) ≥ len s − n (5) cat (takeN n s) (dropN n s) = s 21 [23] Zusammenfassung I Datentypen können rekursiv sein I Rekursive Datentypen sind unendlich (induktiv) I Funktionen werden rekursiv definiert I Formen der Rekursion: linear, baumartig, wechselseitig I Rekursive Definition ermöglicht induktiven Beweis I Nächste Woche: Abstraktion über Typen (Polymorphie) 23 [23] (6) 22 [23]