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