Technische Universität München
Grundlagen der Programm- und Systementwicklung
Funktionale Programmierung
Teil 2 Methodik:
Spezifikation, Implementierung, Verifikation
Technische Universität München
Institut für Informatik
Software & Systems Engineering
Prof. Dr. Dr. h. c. Manfred Broy
Unter Mitarbeit von Dr. Alexander Malkis, Diego Marmsoler,
Veronika Bauer
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun.
Progr.
1
Technische Universität München
Vorlesungsplanung (siehe Webseite)
Einführung
Grundbegriffe und Definitionen, Bedeutung und Stellenwert Kernaufgaben,
Aktueller Stand der Forschung und Praxis
Themen
Daten und Rechenstrukturen
Konzepte
Spezifikation
Verifikation von Eigenschaften
Funktionale Programme
Konzepte
Spezifikation
Verifikation von Eigenschaften
Anweisungs- und objektorientierte Programme
Spezifikation
Verifikation von Eigenschaften
Referenzen und Zeiger
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
2
Technische Universität München
Methodik funktionaler Programmierung
Spezifikation
Entwurf und Implementierung
Verifikation
Partielle Korrektheit
Terminierung
Konzepte funktionaler Programmierung
Funktionen höherer Ordnung
Currying
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
3
Technische Universität München
Spezifikation funktionaler Programme
Funktionale Programme bestehen aus einer Familie rekursiver Deklarationen
von Funktionen.
Die Spezifikation eines funktionalen Programms entspricht somit
der Angabe der Funktionalität/Sorten und
der Charakterisierung dieser Familie von Funktionen durch logische Eigenschaften.
Algebraische Spezifikation einer Funktion f:
die Funktionalität von f (ihre Funktionssorte),
eine Anzahl von Gleichungsaxiomen und ggf. Definiertheitsaxiome
Prädikative Spezifikation einer Funktion f:
Funktionen werden durch ihre charakteristischen Eigenschaften beschrieben,
nicht nur Gleichungen sondern allgemeine (auch nichtmonotone) Prädikate,
es wird ein beliebiger prädikatenlogischer Ausdruck für die Spezifikation verwendet,
ein verbreitetes Format: zwei Prädikate – „Vor- und Nachbedingung“ –
Assumption/Commitment
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
4
Technische Universität München
Mustersuche in einem Text
Gegeben:
Text
x : Seq Char
und Muster
m : Seq Char .
Gesucht: Funktion
search: Seq Char, Seq Char Nat ,
die eine Position des Musters (wenn vorhanden) im Text liefert, d.h. mit
( i Nat: found(x, i, m) = true) ⇒ found(x, search(x, m), m) = true ,
wobei
found: Seq Char, Nat, Seq Char Bool
found(x, n, m) = s, r Seq Char: x = s ∘ m ∘ r n = #s
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
5
Technische Universität München
Mustersuche in einem binären gewurzelten Baum
Gegeben:
Text
x : Tree Char
und Muster
m : Tree Char
Gesucht: Funktion
search: Tree Char, Tree Char Seq Bool
die eine Position des Musters (wenn vorhanden) im Baum liefert, d.h. mit
( p Seq Bool: found(x, p, m) = true) found(x, search(x, m), m) = true ,
wobei
found: Tree Char, Seq Bool, Tree Char Bool
found(x, ‹›, m) = (x = m)
found(etree, ‹b› p, m) = false
found(x, ‹false› p, m) = found(left(x), p, m)
found(x, ‹true› p, m) = found(right(x), p, m)
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
6
Technische Universität München
Erreichbarkeit in Graphen
Gegeben:
Graph durch Funktion
g: Node Fset Node
liefert zu jedem Knoten n die Menge g(n) der durch eine Kante
erreichbaren Knoten
Gesucht: Funktion
er: Node Fset Node
die zu jedem Knoten n die Menge er(n) der in g durch eine Folge von
Kanten (einen „Pfad“) erreichbaren Knoten
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
7
Technische Universität München
Prädikative Spezifikation
m er(n) = s Seq Node: ispath(n, s, m)
wobei
ispath : Node, Seq Node, Node Bool
durch die folgende algebraische Spezifikation beschrieben wird:
ispath(n, ‹›, m) = (n=m)
ispath(n, ‹a› ∘ s, m) = (isel(g(n), a) ispath(a, s, m))
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
8
Technische Universität München
Spezifikation vs. funktionales Programm
Für ein Funktionssymbol f mit Funktionalität
f : M1 , ... , Mn Mn+1
und ein spezifizierendes Prädikat (Spezifikation einer Funktion)
R: (M1 , ... , Mn Mn+1) IB
formulieren wir die Aussage, dass f die Spezifikation R erfüllt:
f sat R
(oder auch R(f))
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
9
Technische Universität München
Assumption/Commitment - Spezifikation
Für ein Funktionssymbol f mit Funktionalität
f : M1 , ... , Mn Mn+1
erhalten wir eine Spezifikation
assume(x1 , ... , xn) commit(x1 , ... , f(x1 , ... , xn))
beschrieben durch die Prädikate
assume : M1 , ... , Mn Bool
commit : M1 , ... , Mn+1 Bool
Beispiel:
insort: Seq Nat, Seq Nat Seq Nat
is_sorted(s) (is_sorted(insort(r, s)) insort(r, s) r ∘ s )
mit
is_sorted: Seq Nat Bool
Sequenz ist sortiert
_ _: Seq Nat, Seq Nat Bool Sequenzen haben gleiche Elemente
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
10
Technische Universität München
BEWEISEN VON EIGENSCHAFTEN FÜR
FUNKTIONALE PROGRAMME
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
11
Technische Universität München
Deklaration – Aussagen
Gegeben rekursive Deklaration
fct f = (x1 : M1, ..., xn : Mn)Mn+1 : t
Für f gilt die Gleichung:
f(x1, ..., xn) = t
Weiter gilt: f ist die am wenigsten definierte Funktion, die die Gleichung
erfüllt:
( x1, ..., xn : g(x1, ..., xn) = t[g/f] ) f ⊑ g
Wenn wir Aussagen über f beweisen wollen, können wir uns auf diese
Eigenschaft stützen.
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
12
Technische Universität München
Beweisprinzipien für Rekursion
Berechnungsinduktion
Beweise durch Fixpunkteigenschaften
Strukturelle Induktion
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
13
Technische Universität München
Beweisprinzipien für Rekursion: Berechnungsinduktion
Die Berechnungsinduktion:
erlaubt induktive Beweise von Eigenschaften für rekursiv deklarierte
Funktionen, falls das auftretende Funktional stetig ist.
das zu beweisende Prädikat muss gewisse Eigenschaften haben.
Zulässigkeit des Prädikates.
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
14
Technische Universität München
Beweisprinzipien für Rekursion: Berechnungsinduktion
Sei (X,⊑) eine vollständige Halbordnung. Ein Prädikat
φ: X → IB
heißt zulässig (engl. admissible), falls für jede gerichtete
Menge N⊆X gilt:
(∀ a∈N: φ(a)) ⇒ φ(sup N) .
Beachte:
Unsere Definition der Vollständigkeit für Halbordnungen fordert die
Existenz von Suprema für alle gerichtete Mengen, insbesondere für die
leere Menge. Ist also ⊥∈X das kleinste Element einer vollständigen
Halbordnung, so gilt φ(⊥) für jedes zulässige Prädikat φ auf X.
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
15
Technische Universität München
Beispiele: ein zulässiges und ein unzulässiges Prädikat
Ist (X,⊑) eine vollständige Halbordnung und g ∊ X, so ist
φ: X→𝔹, f ↦ (f⊑g) ein zulässiges Prädikat auf X.
Beweis:
Sei N⊆X gerichtet mit ∀ f∊N: φ(f). Dann gilt ∀ f∊N: f⊑g, also sup⊑(N) ⊑ g.
Sei ℕ⊥ mit flacher Halbordnung ≼ versehen, und sei ℕ⊥→ℕ⊥
punktweise partiell geordnet:
f ⊑ g gdw. ∀ x∊ℕ⊥: f(x) ≼ g(x) (f,g ∈ ℕ⊥→ℕ⊥).
Dann ist (ℕ⊥→ℕ⊥, ⊑) eine vollständige Halbordnung und
φ: (ℕ⊥→ℕ⊥)→𝔹, f ↦ (id⋢f) ein nicht zulässiges Prädikat, wobei id=idℕ⊥.
Beweis:
(ℕ⊥→ℕ⊥, ⊑) vollständige Halbordnung: ist F ⊆ (ℕ⊥→ℕ⊥) gerichtet, so ist
λ x∊ℕ⊥. sup≼{f(x) | f ∊ F} wohldefiniert und gleich sup⊑(F).
φ nicht zulässig:
N = { f: ℕ⊥→ℕ⊥ | f(⊥)=⊥ ∧ ∃ n∈ℕ: ((∀ x<n: f(x)=x) ∧ (∀ x≥n: f(x)=⊥)) }
ist gerichtet, id ⋢ f gilt für alle f ∊ N, aber sup⊑(N) = id.
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
16
Technische Universität München
Beweisprinzipien für Rekursion: Berechnungsinduktion
Hierbei ist fix τ der kleinste Fixpunkt von τ.
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
17
Technische Universität München
Beweise durch Fixpunkteigenschaften
Aus den Fixpunkteigenschaften lassen sich Eigenschaften für eine
rekursiv deklarierte Funktion f beweisen.
Dies funktioniert aber nur für Eigenschaften, die für alle Fixpunkte
gelten.
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
18
Technische Universität München
Beweise durch Fixpunkteigenschaften
Fixpunkt von τ ist.
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
19
Technische Universität München
Beweise durch Fixpunkteigenschaften
Um τ(g)=g zu zeigen, zeigen wir per Induktion über a, dass
∀a,b ∈ Nat: (τ(g))(a,b) = g(a,b) :
Für alle a,b ∈ Nat gilt:
(τ(g))(a,b)
= if a=0 then b else g(a-1, a*b) fi
= if a=0 then b else (a-1)!*a*b fi
= if a=0 then b else a!*b fi
= a!*b
= g(a,b) .
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
20
Technische Universität München
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
21
Technische Universität München
Beweisprinzipien für Rekursion: Strukturelle Induktion
Schließlich können wir Beweise über rekursiv definierte Funktionen
auch führen, indem wir klassische Beweisprinzipien der Induktion
bezüglich der Argumentbereiche zusammen mit der
Fixpunkteigenschaft verwenden.
Dies erfordert eine Noethersche Ordnung auf dem Definitionsbereich.
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
22
Technische Universität München
Beweisprinzipien für Rekursion: Induktion
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
23
Technische Universität München
Beispiel: Induktion über ℕ
Lemma: ∀ s,t ∈ Seq α : reverse(s∘t) = reverse(t)∘reverse(s).
Beweis: Durch Induktion über die Länge von s.
Sei s ∈ Seq α beliebig, fest. Wir nehmen an, dass
∀ s’ ∈ Seq α : |s’|<|s| ⇒ ∀ t ∈ Seq α : reverse(s’∘t) = reverse(t)∘reverse(s’)
und zeigen ∀ t ∈ Seq α : reverse(s∘t) = reverse(t)∘reverse(s).
Sei t ∈ Seq α beliebig, fest.
Falls s=ε, so reverse(s∘t) = reverse(t) = reverse(t)∘ε = reverse(t)∘reverse(s).
Falls s=〈a〉 und t=ε, so reverse(s∘t) = reverse(〈a〉) = reverse(s)∘reverse(t).
Falls s=〈a〉 und t≠ε, so reverse(s∘t) = reverse(〈a〉∘t) = reverse(t)∘〈a〉
= reverse(t)∘reverse(s).
Falls ε≠s≠〈a〉, so reverse(s∘t) [Axiome über first, rest]
= reverse((〈first(s)〉∘rest(s))∘t) [Assoziativität]
= reverse(〈first(s)〉∘(rest(s)∘t)) [Def. von reverse]
= reverse(rest(s)∘t)∘〈first(s)〉 [Induktions-Annahme]
= (reverse(t)∘reverse(rest(s)))∘〈first(s)〉 [Assoziativität]
= reverse(t)∘(reverse(rest(s))∘〈first(s)〉) [Def. von reverse]
= reverse(t)∘reverse(〈first(s)〉∘rest(s)) [Axiome über first, rest]
= reverse(t)∘reverse(s).
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
24
Technische Universität München
Beispiel: strukturelle Induktion
Satz: ∀ s ∈ Seq α : reverse(reverse(s)) = s.
Beweis: Durch Induktion über die Teilzeichenkette-Relation.
Sei s ∈ Seq α beliebig, fest. Wir nehmen an, dass
∀ r ∈ Seq α : r echte Teilzeichenkette von s ⇒ reverse(reverse(r)) = r.
und zeigen reverse(reverse(s)) = s.
Falls s=ε, so reverse(reverse(s)) = reverse(reverse(ε)) = reverse(ε)
= ε = s.
Falls s=〈a〉, so reverse(reverse(s)) = reverse(〈a〉) = 〈a〉.
Falls ε≠s≠〈a〉, so reverse(reverse(s)) [Axiome über first, rest]
= reverse(reverse((〈first(s)〉∘rest(s))) [Def. von reverse]
= reverse(reverse(rest(s))∘〈first(s)〉) [Lemma]
= reverse(〈first(s)〉)∘reverse(reverse(rest(s))) [Def. von reverse]
= 〈first(s)〉∘reverse(reverse(rest(s))) [Induktions-Annahme]
= 〈first(s)〉∘rest(s) [Axiome über first, rest]
= s.
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
25
Technische Universität München
Terminierungsbeweise
Abstiegsfunktion (Bewertungsfunktion)
Beweis: Schema
Beispiele
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
26
Technische Universität München
Terminierungsbeweise
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
27
Technische Universität München
Terminierungsbeweise
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
28
Technische Universität München
Terminierungsbeweise
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
29
Technische Universität München
Terminierungsbeweis: Beispiel
Sei p(x,y) = (x=0 ∨ y>0).
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
30
Technische Universität München
Terminierungsbeweis: Beispiel
Seien feste x,y ∈ Nat mit p(x,y) gewählt, d.h. x=0 ∨ y>0.
Wir nehmen an, dass für alle (x’,y’) mit p(x’,y’) und h(x’,y’)<h(x,y)
die Formel Def[f(x’,y’)] gilt. Wir zeigen, dass Def[f(x,y)] gilt.
• Induktionsanfang (Fall h(x,y)=0):
Wegen h(x,y)=0 gilt x≤y. Dann ist
t[Ω/f] = if x≤y then y else Ω(x,2*y) fi = y
definiert.
• Induktionsschritt (Fall h(x,y)>0):
Wegen h(x,y)>0 ist if x≤y then 0 else x-y fi > 0, also h(x,y) =
x-y und x>y. Dann x≠0. Wegen p(x,y) muss also y>0 gelten.
Daher ist 2*y>y und x-2*y < x-y, daher
h(x,2*y) ≤ x-2*y < h(x,y). Nach Induktionsannahme gilt
Def[f(x,2*y)]. Dann gilt auch
Def[if x≤y then y else f(x,2*y) fi].
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
31
Technische Universität München
Funktionale Sorten
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
32
Technische Universität München
Funktionale Sorten: Beispiel
1. Stufe:
2. Stufe:
2. Stufe:
3. Stufe:
f1: Nat → Nat
f2: (Nat → Nat), Nat → Nat
f3: Nat → (Nat → Nat)
f4: ((Nat → Nat), Nat → Nat), Nat → (Nat → Nat)
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
33
Technische Universität München
Funktionsabstraktion
Eine Funktionsabstraktion ist ein Term
(x1: M1, …, xn: Mn) Mn+1 : t ,
wobei
M1, …, Mn+1 Sorten sind,
falls jedes xi im Term t die Sorte Mi hat (1≤i≤n),
so hat t die Sorte Mn+1.
Die freien Identifikatoren (freien Variablen) der Funktionsabstraktion wie oben
sind die freien Variablen von t ohne x1, …, xn.
Beispiel:
Sei fct f2 = (g: (Nat)Nat, z: Nat) Nat : g(z)+z .
Der Term f2((x: Nat)Nat: x+succ(0), succ(0))
= ((g: (Nat)Nat, z: Nat) Nat : g(z)+z) ((x: Nat)Nat: x+succ(0), succ(0))
kann zu ((x: Nat)Nat: x+succ(0)) (succ(0))+succ(0) und dann zu
(succ(0)+succ(0))+succ(0) reduziert werden. Über ℕ ist die Interpretation des
letzten Terms gleich 3.
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
34
Technische Universität München
Currying
Funktion curryi (sei i ℕ, 1 i n)
Benannt nach dem Logiker Haskell Curry (1900-1982),
gleichzeitig Namenspatron für die funktionale
Programmiersprache Haskell.
Statt curry1 schreiben wir curry.
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
35
Technische Universität München
Currying: Beispiel
Sei
add: Nat, Nat → Nat
mit Interpretation der Addition über ℕ.
Dann ist
curry(add): Nat → Nat → Nat
mit
curry(add)(n) = (m: Nat) Nat: add(n,m) .
Die Interpretation des Terms
curry(add)(succ(succ(succ(succ(succ(0))))))
über ℕ ist die Funktion, die zu jedem Argument 5
hinzuzählt.
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
36
Technische Universität München
Funktionskomposition und Formale Parameter
Relationale Komposition f ∘ g zweier Funktionen f, g mit
f: M1 M2
g: M2 M3
ist eine polymorphe Funktion höherer Stufe mit Funktionssorte
f ∘ g : (1 2), (2 3) (1 3)
Es gilt:
(f ∘ g)(x) = g(f(x))
Gebundene vs. freie Bezeichnungen: Beispiel
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
37
Technische Universität München
Tupel
Tupelsorten: [M1, ..., Mn]
Ähnlich zu Produktsorten und Records
Tupelbildende Funktion [ _, ..., _ ] : M1, ..., Mn [M1, ..., Mn]
Strikte Tupelbildung
Tupelbildende Funktion wird als strikt angenommen.
Tupel enthalten dann niemals das Pseudoelement als Eintrag.
Mengenprodukt als Trägermenge für die Tupelsorte [M1, ..., Mn] :
((M1 \ {}) ... (Mn \ {}) ) {}
Nichtstrikte Tupelbildung
Tupelbildende Funktion wird als nichtstrikt angenommen.
Mengenprodukt als Trägermenge für die Tupelsorte [M1, ..., Mn] :
(M1 ... Mn) \ {(,..., )} {}
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
38
Technische Universität München
Zwei schwächere Formen der Erfüllung einer Spezifikation
Eine Funktion f ist partiell korrekt bezüglich der Spezifikation R:
f satpar R
g: f ⊑ g R(g)
f liefert Resultate, wie in der Spezifikation gefordert, falls sie terminiert.
Die Terminierung von f ist jedoch nicht garantiert, selbst wenn sie in der
Spezifikation R gefordert wird.
Eine Funktion f ist robust korrekt bezüglich der Spezifikation R:
f satrob R
g: g ⊑ f R(g)
f terminiert für alle Fälle, die durch die Spezifikation gefordert werden
f liefert dann Werte wie spezifiziert.
Sogar in Fällen, in denen die Spezifikation keine Terminierung fordert und keine
Werte vorschreibt, kann f terminieren (und beliebig gewählte Werte liefern).
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
39
Technische Universität München
Partielle und robuste Korrektheit: Beispiele
fct div1 = (x,y: Nat) Nat: if x<y then 0 else div1(x-y, y)+1 fi
fct div2 = (x,y: Nat) Nat: if y=0 ∨ x<y then 0 else div2(x-y, y)+1 fi
fct div3 = (x,y: Nat) Nat: if x<y then 0 else div3(x-2*y, y)+2 fi
(mit ∀ x,y∈ Nat : x<2*y ⇒ x-2*y=⊥)
R(f)
=
∀ x,y∈ Nat: ( ( y>0 ⇒ y*f(x,y) ≤ x < y*(f(x,y)+1) )
∧ ( y=0 ⇒ f(x,y) = ⊥) )
div1 sat R, da R(div1).
div2 satrob R, da div1⊑div2 ∧ R(div1).
div3 satpar R, da div3⊑div1 ∧ R(div1).
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
40
Technische Universität München
Abschließende Bemerkungen
Funktionale Programmierung
Vorteile
wenige einfache transparente Konzepte
klare Semantik
hohe Unabhängigkeit von technischen Details
hohe Modularität
Nachteile
Heutige Maschinen arbeiten nach dem von Neumann-Konzept
(Prozessor/Speicher/Instruktion)
Hohe Effizienz auf heute gängigen Maschinen nicht vollständig erreichbar
M. Broy WS 13/14 : Grundlagen der Programm- und Systementwicklung: Fun. Progr.
41