Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Übersicht 1 Einleitung Einführung in die Funktionale Programmierung: 2 Normalordnungsreduktion Funktionale Kernsprachen: 3 Anwendungsordnung 4 Verzögerte Auswertung 5 Programmieren mit let in Haskell 6 Gleichheit Einleitung & Der Lambda-Kalkül Dr. David Sabel WS 2010/11 Stand der Folien: 25. Oktober 2010 EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 1 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit 2/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Compilerphasen des GHC (schematisch) Einleitung Wir betrachten zunächst den Lambda-Kalkül Er ist Kernsprache“ fast aller (funktionalen) ” Programmiersprachen Allerdings oft zu minimalistisch, später: Erweiterte Lambda-Kalküle Kalkül: Syntax und Semantik Sprechweise: der Kalkül (da mathematisch) EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 3/54 EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 4/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Kalküle Ansätze zur Semantik Axiomatische Semantik Beschreibung von Eigenschaften von Programmen mithilfe logischer Axiome und Schlussregeln Syntax Legt fest, welche Programme (Ausdrücke) gebildet werden dürfen Herleitung neuer Eigenschaften mit den Schlussregeln Prominentes Beispiel: Hoare-Logik, z.B. Hoare-Tripel {P }S{Q}: Welche Konstrukte stellt der Kalkül zu Verfügung? Vorbedingung P , Programm S, Nachbedingung Q Semantik Schlussregel z.B.: Legt die Bedeutung der Programme fest Gebiet der formalen Semantik kennt verschiedene Ansätze → kurzer Überblick auf den nächsten Folien Sequenz: {P }S{Q}, {Q}T {R} {P }S;T {R} Erfasst i.a. nur einige Eigenschaften, nicht alle, von Programmen EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 5/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 6/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Ansätze zur Semantik (2) Ansätze zur Semantik (3) Denotationale Semantik Operationale Semantik Abbildung von Programmen in mathematische Räume durch Semantische Funktion definiert genau die Auswertung/Ausführung von Programmen Oft Verwendung von partiell geordneten Mengen (Domains) definiert quasi einen Interpreter Z.B. J · K als semantische Funktion: JbK, falls JaK = True JcK, falls JaK = False Jif a then b else cK = ⊥, sonst Verschiedene Formalismen: Zustandsübergangssysteme Abstrakte Maschinen Ersetzungssysteme Gilt i.a. als mathematisch elegant Wir verwenden operationale Semantiken Unterscheidung in small-step und big-step Semantiken Schwierigkeit steigt mit dem Umfang der Syntax EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 7/54 EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 8/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Der Lambda-Kalkül Syntax des Lambda-Kalküls Expr ::= V Von Alonzo Church und Stephen Kleene in den 1930er Jahren eingeführt Variable (unendliche Menge) | λV.Expr | (Expr Expr) Anwendung (Applikation) Abstraktion Der Lambda-Kalkül ist Turing-mächtig. Wir betrachten den ungetypten Lambda-Kalkül. Abstraktionen sind anonyme Funktionen Ausdrücke des Lambda-Kalküls können in Haskell eingegeben werden, aber sie müssen korrekt getypt sein Z. B. id(x) = x in Lambda-Notation λx.x Haskell: \x -> s statt λx.s (s t) erlaubt die Anwendung von Funktionen auf Argumente s, t beliebige Ausdrücke =⇒ Higher-Order Lambda Kalkül Z. B. (λx.x) (λx.x) (entspricht gerade id(id)) EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 9/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 10/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Syntax des Lambda-Kalküls (2) Freie und gebundene Vorkommen von Variablen Assoziativitäten, Prioritäten und Abkürzungen Durch λx ist x im Rumpf s von λx.s gebunden. Kommt x in t vor, so Klammerregeln: s r t entspricht (s r) t Priorität: Rumpf einer Abstraktion so groß wie möglich: ist das Vorkommen frei, wenn kein λx darüber steht λx.x y ist λx.(x y) und nicht ((λx.y) x) anderenfalls ist das Vorkommen gebunden λx, y, z.s entspricht λx.λy.λz.s Beispiel: (λx.λy.λw.(x y z)) x Prominente Ausdrücke I K K2 Ω Y := := := := := λx.x λx.λy.x λx.λy.y (λx.(x x)) (λx.(x x)) λf.(λx.(f (x x))) (λx.(f (x x))) EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel x kommt je einmal gebunden und frei vor y kommt gebunden vor z kommt frei vor 11/54 EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 12/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Freie und gebundene Variablen Substitution Menge der freien und gebundenen Variablen FV (t): Freie Variablen von t FV (x) =x FV (λx.s) = FV (s) \ {x} FV (s t) = FV (s) ∪ FV (t) s[t/x] = ersetze alle freien Vorkommen von x in s durch t BV (t): Gebundene Var. von t BV (x) =∅ BV (λx.s) = BV (s) ∪ {x} BV (s t) = BV (s) ∪ BV (t) Formale Definition O.B.d.A. sei x 6∈ BV (s) x[t/x] y[t/x] (λy.s)[t/x] (s1 s2 )[t/x] Wenn FV (t) = ∅, dann sagt man: t ist geschlossen bzw. t ist ein Programm = = = = t y, falls x 6= y λy.(s[t/x]) (s1 [t/x] s2 [t/x]) Anderenfalls: t ist ein offener Ausdruck Z.B. (λx.z x)[(λy.y)/z] = (λx.((λy.y) x)) Z.B. BV (λx.(x (λz.(y z)))) = {x, z} FV (λx.(x (λz.(y z)))) = {y} EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 13/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 14/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Kontexte Alpha-Äquivalenz Alpha-Umbenennungsschritt Kontext = Ausdruck, der an einer Position ein Loch [·] anstelle eines Unterausdrucks hat α C[λx.s] − → C[λy.s[y/x]] falls y 6∈ BV (λx.s) ∪ FV (λx.s) Als Grammatik: Alpha-Äquivalenz C = [·] | λV.C | (C Expr) | (Expr C) α =α ist die reflexiv-transitive Hülle von − → Sei C ein Kontext und s ein Ausdruck s: C[s] = Ausdruck, in dem das Loch in C Wir betrachten α-äquivalente Ausdrücke als gleich. z.B. λx.x =α λy.y durch s ersetzt wird Distinct Variable Convention: Alle gebundenen Variablen sind verschieden und gebundene Variablen sind verschieden von freien. Beispiel: C = ([·] (λx.x)), dann: C[λy.y] = ((λy.y) (λx.x)). Das Einsetzen darf/kann freie Variablen einfangen: z.B. sei C = (λx.[·]), dann C[λy.x] = (λx.λy.x) EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel α-Umbenennungen ermöglichen, dass die DVC stets erfüllt werden kann. 15/54 EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 16/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Beispiel zur DVC und α-Umbenennung Operationale Semantik - Beta-Reduktion Beta-Reduktion (y (λy.((λx.(x x)) (x y)))) (λx.s) t → s[t/x] (β) =⇒ erfüllt die DVC nicht. β Wenn s − → t, dann sagt man s reduziert unmittelbar zu t. Beispiele: (y (λy.((λx.(x x)) (x y)))) − → (y (λy1 .((λx.(x x)) (x y1 )))) α − → (y (λy1 .((λx1 .(x1 x1 )) (x y1 )))) α β (λx. |{z} x ) (λy.y) − → x[(λy.y)/x] = λy.y | {z } s (y (λy1 .((λx1 .(x1 x1 )) (x y1 )))) erfüllt die DVC β (λy. y y y ) (x z) − → (y y y)[(x z)/y] = (x z) (x z) (x z) | {z } | {z } s EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel t 17/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit t EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 18/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Beta-Reduktion: Umbenennungen Operationale Semantik Für die Festlegung der operationalen Semantik, muss man noch definieren, wo die β-Reduktion angewendet wird Damit die DVC nach einer β-Reduktion gilt, muss man umbenennen: Betrachte ((λx.xx)((λy.y)(λz.z))). β ((λx.xx)((λy.y)(λz.z))) → ((λy.y)(λz.z)) ((λy.y)(λz.z)) oder (λx.(x x)) (λy.y) − → (λy.y) (λy.y) =α (λy1 .y1 ) (λy2 .y2 ) ((λx.xx)((λy.y)(λz.z))) → ((λx.xx)(λz.z)). EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 19/54 EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 20/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Normalordnungsreduktion Reduktionskontexte: Beispiele Zur Erinnerung: R ::= [·] | (R Expr) Sprechweisen: Normalordnung, call-by-name, nicht-strikt, lazy Grob: Definitionseinsetzung ohne Argumentauswertung Sei s = ((λw.w) (λy.y)) ((λz.(λx.x) z) u) Definition Reduktionskontexte R ::= [·] | (R Expr) Alle Reduktionskontexte für s“, d.h. R mit R[t] = s ” R = [·], Term t ist s selbst, für s ist aber keine β-Reduktion möglich β no −→: Wenn s − → t, dann ist no R[s] −→ R[t] R = ([·] ((λz.(λx.x) z) u)), Term t ist ((λw.w) (λy.y)) eine Normalordnungsreduktion β Reduktion möglich: ((λw.w) (λy.y)) − → (λy.y). no Daher s = R[((λw.w) (λy.y))] −→ R[λy.y] = ((λy.y) ((λz.(λx.x) z) u)) ((λx.(x x)) (λy.y)) ((λw.w) (λz.(z z))) β Beispiel: − → = (x x)[(λy.y)/x] ((λw.w) (λz.(z z))) ((λy.y) (λy.y)) ((λw.w) (λz.(z z))) R = ([·] (λy.y)) ((λz.(λx.x) z) u), Term t ist (λw.w), für (λw.w) ist keine β-Reduktion möglich. R = ([·] (λz.(z z))) EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 21/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 22/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Redexsuche: Markierungsalgorithmus Beispiel s ein Ausdruck. Start: s? ⇒ ⇒ Verschiebe-Regel ? (s1 s2 ) ⇒ (s?1 s2 ) so oft anwenden wie möglich. no −→ ((λy.((λw.w)(λz.z)))(λu.u))? ⇒ ((λy.((λw.w)(λz.z)))? (λu.u)) Beispiel 1: (((λx.x) (λy.(y y))) (λz.z))? Beispiel 2: (((y z) ((λw.w)(λx.x)))(λu.u))? Allgemein: Ergebnis (s?1 (((λx.λy.x)((λw.w)(λz.z)))(λu.u))? (((λx.λy.x)((λw.w)(λz.z)))? (λu.u)) (((λx.λy.x)? ((λw.w)(λz.z)))(λu.u)) no −→ ((λw.w)(λz.z))? ⇒ ((λw.w)? (λz.z)) s2 . . . sn ), wobei s1 keine Anwendung Falls s1 = λx.t und n ≥ 2 dann reduziere: no −→ (λz.z) no (λx.t) s2 . . . sn −→ (t[s2 /x] . . . sn ) EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 23/54 EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 24/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Normalordnungsreduktion: Eigenschaften (1) Normalordnungsreduktion: Eigenschaften (2) Die Normalordnungsreduktion ist deterministisch: Weitere Notationen: no Für jeden Ausdruck s gibt es höchstens ein t, so dass s −→ t. no,+ no −−−→ = transitive Hülle von −→ no,∗ no −−→ = reflexiv-transitive Hülle von −→ Ausdrücke, für die keine Reduktion möglich ist: Definition Reduktion stößt auf freie Variable: z.B. (x (λy.y)) no,∗ Ein Ausdruck s konvergiert ( s ⇓ ) gdw. ∃FWHNF v : s −−→ v. Andernfalls divergiert s, Notation s ⇑ Ausdruck ist eine FWHNF: FWHNF = Abstraktion (functional weak head normal form) EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 25/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 26/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Anmerkungen Church-Rosser Theorem Haskell verwendet den call-by-name Lambda-Kalkül als semantische Grundlage Für den Lambda-Kalkül gilt: Implementierungen verwenden call-by-need Variante: Vermeidung von Doppelauswertungen (kommt später) Church-Rosser Eigenschaft: ∗ ∗ ∗ Wenn a ← → b, dann existiert c, so dass a → − c und b → − c Call-by-name (und auch call-by-need) sind optimal bzgl. Konvergenz: a >o EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel > ∗ Aussage Sei s ein Lambda-Ausdruck und s kann mit beliebigen β-Reduktionen (an beliebigen Positionen) in eine Abstraktion v überführt werden. Dann gilt s ⇓. /b ∗ > > ∗ c ∗ Hierbei meint → − beliebige β-Reduktionen (in bel. Kontext) 27/54 EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 28/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Anwendungsordnung Redexsuche mit Markierungsalgorithmus Sprechweisen: Anwendungsordnung, call-by-value, strikt Starte mit s? Grobe Umschreibung: Argumentauswertung vor Definitionseinsetzung Wende die Regeln an solange es geht: • (s1 s2 )? ⇒ (s?1 s2 ) Call-by-value Beta-Reduktion • (v ? s) ⇒ (v s? ) falls v eine Abstraktion oder Variable und s keine Abstraktion oder Variable (λx.s) v → s[v/x], wobei v Abstraktion oder Variable (βcbv ) Definition CBV-Reduktionskontexte E: Beispiel: ((((λx.x) (((λy.y) v) (λz.z))) u) (λw.w))? E ::= [·] | (E Expr) | ((λV.Expr) E) Falls danach gilt: C[(λx.s)? v] dann β cbv Wenn s −− → t, ao dann ist E[s] −→ E[t] eine Anwendungsordnungsreduktion EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel ao C[(λx.s)? v] −→ C[s[v/x]] 29/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 30/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Beispiel Eigenschaften ⇒ ⇒ ⇒ ⇒ (((λx.λy.x)((λw.w)(λz.z)))(λu.u))? (((λx.λy.x)((λw.w)(λz.z)))? (λu.u)) (((λx.λy.x)? ((λw.w)(λz.z)))(λu.u)) (((λx.λy.x)((λw.w)(λz.z))? )(λu.u)) (((λx.λy.x)((λw.w)? (λz.z)))(λu.u)) Auch die call-by-value Reduktion ist deterministisch. Definition Ein Ausdruck s call-by-value konvergiert ( s ⇓ao ), gdw. ao,∗ ∃ FWHNF v : s −−→ v. Ansonsten (call-by-value) divergiert s, Notation: s ⇑ao . ao −→ (((λx.λy.x)(λz.z))(λu.u))? ⇒ (((λx.λy.x)(λz.z))? (λu.u)) ⇒ (((λx.λy.x)? (λz.z))(λu.u)) es gilt: s ⇓ao =⇒ s ⇓. Die Umkehrung gilt nicht! Vorteil der Anwendungsordnung: ao −→ ((λy.λz.z)(λu.u))? ⇒ ((λy.λz.z)? (λu.u)) Tlw. besseres Platzverhalten Seiteneffekte können direkt eingebaut werden, da die Auswertungsreihenfolge fest liegt. ao −→ (λz.z) EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 31/54 EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 32/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit In Haskell: seq Beispiel zu seq fak 0 = 1 fak x = x * fak(x-1) Auswertung von fak n: In Haskell: Strikte Auswertung kann mit seq erzwungen werden. seq a b = b falls a ⇓ (seq a b) ⇑ falls ⇑ --> --> --> --> --> --> fak n * n * ... n * n * ... n fak (n-1) ((n-1) * fak (n-2)) ((n-1) * ((n-2) * .... * (2 * 1))) ((n-1) * ((n-2) * .... * 2)) Problem: Linearer Platzbedarf EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 33/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 34/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Beispiel zu seq (2) Beispiele Version mit seq: fak’ x = fak’’ x 1 fak’’ 0 y = y fak’’ x y = let m = x*y in seq m (fak’’ (x-1) m) Auswertung in etwa: --> --> --> --> --> --> --> --> --> Ω := (λx.x x) (λx.x x). no Ω −→ Ω. Daraus folgt: Ω ⇑ ao Ω −→ Ω. Daraus folgt: Ω ⇑ao . fak’ 5 fak’’ 5 1 let m=5*1 in seq m (fak’’ (5-1) m) let m=5 in seq m (fak’’ (5-1) m) (fak’’ (5-1) 5) (fak’’ 4 5) let m = 4*5 in seq m (fak’’ (4-1) m) let m = 20 in seq m (fak’’ (4-1) m) (fak’’ (4-1) 20) ... t := ((λx.(λy.y)) Ω). no t −→ λy.y, d.h. t ⇓. Da die Anwendungsordnung zunächst das Argument Ω auswerten muss, gilt t ⇑ao . Nur konstanter Platzbedarf, da Zwischenprodukt m berechnet wird (erzwungen durch seq). EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 35/54 EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 36/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Verzögerte Auswertung Call-by-Need Lambda Kalkül - Auswertung (1) Sprechweisen: Verzögerte Auswertung, call-by-need, nicht-strikt, lazy, Sharing Reduktionskontexte Rneed : Optimierung der Normalordnungsreduktion Rneed ::= LR[A] | LR[let x = A in Rneed [x]] A ::= [·] | (A Expr) LR ::= [·] | let V = Expr in LR Call-by-need Lambda-Kalkül mit let – Syntax: Expr ::= V | λV.Expr | (Expr Expr) | let V = Expr in Expr A= b links in die Applikation Nicht-rekursives let: in let x = s in t muss gelten x 6∈ FV (t) LR = b Rechts ins let Haskell verwendet rekursives let! EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 37/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 38/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Call-by-Need Lambda Kalkül - Auswertung (2) Markierungsalgorithmus zur Redexsuche (1) need Verzögerter Auswertungsschritt −−−→: Definiert durch 4 Regeln: Markierungen: ?, ¦, } ? ∨ ¦ meint ? oder ¦ (lbeta) Rneed [(λx.s) t] → Rneed [let x = t in s] (cp) Für Ausdruck s starte mit s? . LR[let x = λy.s in Rneed [x]] → LR[let x = λy.s in Rneed [λy.s]] Verschiebe-Regeln: (1) (let x = s in t)? ⇒ (let x = s in t? ) (2) (let x = y ¦ in C[x} ]) ⇒ (let x = y ¦ in C[x]) (3) (let x = s in C[x?∨¦ ]) ⇒ (let x = s¦ in C[x} ]) (4) (s t)?∨¦ ⇒ (s¦ t) (llet) LR[let x = (let y = s in t) in Rneed [x]] → LR[let y = s in (let x = t in Rneed [x])] (lapp) Rneed [(let x = s in t) r] → Rneed [let x = s in (t r)] dabei: immer (2) statt (3) anwenden falls möglich (lbeta) und (cp) statt (β), (lapp) und (llet) zum let-Verschieben EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 39/54 EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 40/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Markierungsalgorithmus zur Redexsuche (2) Beispiel: Verzögerte Auswertung (let x = (λu.u) (λw.w) in ((λy.y) x))? need,lbeta −−−−−−→ (let x = (λu.u) (λw.w) in (let y = x in y))? need,lbeta −−−−−−→ (let x = (letu = λw.w in u) in (let y = x in y))? Reduktion nach Markierung: −−−−−→ (let u = λw.w in (let x = u in (let y = x in y)))? (lbeta) ((λx.s)¦ t) → let x = t in s −−−−→ (cp) need,llet let x = (λy.s)¦ in C[x} ]] → let x = λy.s in C[λy.s] need,cp (let u = (λw.w) in (let x = (λw.w) in (let y = x in y)))? need,cp (let u = (λw.w) in (let x = (λw.w) in (let y = (λw.w) in y)))? −−−−→ need,cp (llet) let x = (let y = s in t)¦ in C[x} ] → let y = s in (let x = t in C[x])] (lapp) ((let x = s in t)¦ r) → let x = s in (t r) EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel −−−−→ (let u = (λw.w) in (let x = (λw.w) in (let y = (λw.w) in (λw.w)))) Der letzte Ausdruck ist eine call-by-need FWHNF Call-by-need FWHNF: Ausdruck der Form LR[λx.s], d.h. let x1 = s1 in (let x2 = s2 in (. . . (let xn = sn in λx.s))) 41/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 42/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Konvergenz und Eigenschaften Einschub: Programmieren mit let in Haskell let in Haskell ist viel allgemeiner als das bisher betrachtete Haskells let für lokale Funktionsdefinitionen: Definition Ein Ausdruck s call-by-need konvergiert (geschrieben als s ⇓need ), let f1 x1,1 . . . x1,n1 = e1 f2 x2,1 . . . x2,n2 = e2 ... fm xm,1 . . . xm,nm = em in . . . need gdw. er mit einer Folge von −−−→-Reduktionen in eine FWHNF überführt werden kann, d.h. need,∗ s ⇓need ⇐⇒ ∃ FWHNF v : s −−−−→ v Definiert die Funktionen f1 , . . . , fm Satz Sei s ein (let-freier) Ausdruck, dann gilt s ⇓ ⇐⇒ s ⇓need . Beispiel: f x y = EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 43/54 let quadrat z = z*z in quadrat x + quadrat y EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 44/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Rekursives let in Haskell Pattern-Matching mit let Verschränkt-rekursives let erlaubt: quadratfakultaet let quadrat z fakq 0 fakq x in fakq x x = = = = z*z 1 (quadrat x)*fakq (x-1) Links in einer let-Bindung darf auch ein Pattern stehen. Beispiel: n P i und i=1 n Q i in einer rekursiven Funktion: i=1 Sharing von Ausdrücken mittels let: verdopplefak x = let fak 0 = 1 fak x = x*fak (x-1) fakx = fak x in fakx + fakx sumprod 1 = (1,1) sumprod n = let (s’,p’) = sumprod (n-1) in (s’+n,p’*n) verdopplefakLangsam x = let fak 0 = 1 fak x = x*fak (x-1) in fak x + fak x Das Paar aus dem rekursiven Aufruf wird mit Pattern Matching am let zerlegt! verdopplefak 100 berechnet nur einmal fak 100, im Gegensatz zu verdopplefakLangsam. EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 45/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 46/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Memoization Memoization (2) Besser: Beispiel: Fibonacci-Zahl -- Fibonacci mit Memoization fibM i = let fibs = [0,1] ++ [fibs!!(i-1) + fibs!!(i-2) | i <- [2..]] in fibs!!i -- i-tes Element der Liste fibs fib 0 = 0 fib 1 = 1 fib i = fib (i-1) + fib (i-2) n 1000 10000 20000 30000 sehr schlechte Laufzeit! n 30 31 32 33 34 35 gemessene Zeit im ghci für fib n 9.75sec 15.71sec 25.30sec 41.47sec 66.82sec 108.16sec EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel gemessene Zeit im ghci für fibM n 0.05sec 1.56sec 7.38sec 23.29sec Bei mehreren Aufrufen, noch besser: fibM’ 47/54 = let fibs = [0,1] ++ [fibs!!(i-1) + fibs!!(i-2) | i <- [2..]] in \i -> fibs!!i -- i-tes Element der Liste fibs EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 48/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit where-Ausdrücke (1) where-Ausdrücke (2) Beachte: (let . . . in e) ist ein Ausdruck, aber e where . . . nicht where kann man um Guards herum schreiben (let nicht): where-Ausdrücke sind ähnlich zu let. f x | x == 0 = a | x == 1 = a*a | otherwise = a*f (x-1) where a = 10 Z.B. sumprod’ 1 = (1,1) sumprod’ n = (s’+n,p’*n) where (s’,p’) = sumprod’ (n-1) Dafür geht f x = \y -> mul where mul = x * y nicht! (da y nicht bekannt in der where-Deklaration) EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 49/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit 50/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Gleichheit Gleichheit (2) Leibnitzsches Prinzip: Zwei Dinge sind gleich, wenn sie die gleichen Eigenschaften haben, bzgl. aller Eigenschaften. Bisher Kalküle: no Call-by-Name Lambda-Kalkül: Ausdrücke, −→, ⇓ Für Kalküle: Zwei Ausdrücke s, t sind gleich, wenn man sie nicht unterscheiden kann, egal in welchem Kontext man sie benutzt. ao Call-by-Value Lambda-Kalkül: Ausdrücke, −→, ⇓ao Call-by-Need Lambda-Kalkül . . . D.h. Syntax + Operationale Semantik. Formaler: s,t sind gleich, wenn für alle C gilt: C[s] und C[t] verhalten sich gleich. Es fehlt: Verhalten muss noch definiert werden. Für deterministische Sprachen reicht die Beobachtung der Terminierung (Konvergenz) Begriff: Wann sind zwei Ausdrücke gleich D.h. insbesondere: Wann darf ein Compiler einen Ausdruck durch einen anderen ersetzen? EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 51/54 EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 52/54 Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Einleitung Normalordnungsreduktion Anwendungsordnung Verzögerte Auswertung Let in Haskell Gleichheit Gleichheit (3) Gleichheit (4) ∼c und ∼ao sind Kongruenzen Kontextuelle Approximation und Gleichheit Call-by-Name Lambda-Kalkül: Kongruenz = Äquivalenzrelation + kompatibel mit Kontexten, d.h. s ∼ t =⇒ C[s] ∼ C[t]. Gleichheit beweisen i.a. schwer, widerlegen i.a. einfach. s ≤c t gdw. ∀C : C[s] ⇓ =⇒ C[t] ⇓ s ∼c t gdw. s ≤c t und t ≤c s Beispiele für Gleichheiten: (β) ⊆∼c (βcbv ) ⊆∼c,ao aber (β) 6⊆∼c,ao Call-by-Value Lambda-Kalkül: ∼c 6⊆∼c,ao und ∼c,ao 6⊆∼c s ≤c,ao t gdw. ∀C : C[s] ⇓ao =⇒ C[t] ⇓ao s ∼c,ao t gdw. s ≤c,ao t und t ≤c,ao s Tiefer gehende Beschäftigung mit Gleichheiten in: M-CEFP Programmtransformationen und Induktion in funktionalen Programmen“ ” EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 53/54 EFP – (03) Lambda-Kalkül – WS 2010/11 – D. Sabel 54/54