Foliensatz 1

Werbung
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
Herunterladen