Teil 3 Standardparadigmen: Deklarative Sprachen funktionales Programmieren: Lisp 1.5 logisches Programmieren: Prolog relationales Programmieren: Datalog Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 1 Lisp 1.5 Urform von Lisp (und aller funktionaler Sprachen) Datenstruktur: Listen (eigentlich Bäume), Atome keine Operationen auf Atomen außer Vergleich, Test Programme sind symbolische Ausdrücke S (S-Ausdrücke), repräsentiert als Listen Programme zur Laufzeit modifizierbar · Atom: Zeichenreihe z ∈ S, spezielle Zeichenreihe nil ∈ S, x · Baum: x,y ∈ S: cons [x,y] = (x . y) ∈ S x Liste: [x1,x2,x3,...,xn] = (x1 x2 x3 ... xn ) = (x1 . (x2 . (x3 . (...(xn . nil)...) 1 · · leere Liste : [ ] = nil 2 Funktionen zur Manipulation von Listen und Atomen: x ··· atom, eq, car, cdr, cons 3 · Grundoperationen: [[lambda ,x], y] = ((lambda x) y) entspricht λx .y (quote x) = 'x entspricht Zeichenreihe x (nicht der Term x!) xn (label n x) entspricht n = x ( cond (x1,y1), (x2,y2),..., (xn,yn)) entspricht if x1≠nil then y1 else (cond (x2,y2),...,(xn,yn)) y x Interpretierer eval berechnet symbolische Ausdrücke durch α-, β-, η−Konversion Literatur: John McCarthy: Comm. of the ACM 3(1960), Nr. 4, 184-195 McCarthy et al.: Lisp 1.5 Programmer's Manual. MIT Press 1965 Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 2 nil Grundfunktionen t bezeichnet beliebigen Wert ≠ nil (nil falsch, t wahr) atom: S → BOOL atom [(x . y)]= nil atom [X] = t eq: Atom × Atom → BOOL eq [X,Y] = nil eq [X,X] = t car: S → S car [(x . y)] = x cdr: S → S cdr [(x . y)] = y cons: S × S → S cons[x,y] = (x . y) Alle nicht angegebenen Fälle liefern nil, insbesondere eq angewandt auf Listen Lisp 1 war die erste verbreitete Sprache mit automatischer Speicherbereinigung! Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 3 Emacs Lisp LaTeX-Filter (find-file (getenv "INFILE")) (replace-string " " "§NeueZeile§" nil) (beginning-of-buffer) (replace-string "§NeueZeile§ §NeueZeile§" "§Absatz§" nil) (beginning-of-buffer) (replace-regexp "\\\\cite{[^#]*}" "" nil) (beginning-of-buffer) ... Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 4 Emacs Lisp Türme von Hanoi (defun hanoi (n von nach zwischen) (cond ((< n 1) ; fertig (t (hanoi (n-1) von zwischen nach) (move von nach) (hanoi (n-1) zwischen nach von))) ) (defun show-move (von nach) ... ) (provide hanoi) Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 5 Der Sündenfall: Zuweisungen in Lisp Alle heutigen Versionen von Lisp (Emacs Lisp, Scheme, Common Lisp, ...) erlauben Zuweisungen: Variablenvereinbarung (in Emacs Lisp): (defvar variable) oder (defvar variable anfangswert) oder (defvar variable anfangswert ''Dokumentation'') Zuweisung: (setq variable wert) Bedeutung: variable zeigtHöhere aufProgrammiersprachen wert (keine Kopie!) Prof. Dr. Gerhard Goos SS 2001 6 Grundprinzipien funktionaler Sprachen Berechnungsmodell: Reduktion von Ausdrücken wie in Lisp Programm ist Folge von Funktionsdefinitionen und ein Ausdruck Schreibweise angelehnt an übliche mathematische Schreibweisen Programm und Daten getrennt Grundtypen: Listen, Terme, übliche Grundtypen (Bool, Int, ...); darauf aufbauend reichhaltiger Typverband starke Typisierung, Verwendung von Typinferenz mehrere Definitionen des gleichen Funktionsbezeichners möglich, Auswahl der Funktionsdefinition bei Anwendung durch Typinferenz und Durchmustern der Parameterlisten, um die erste auf die Argumente zutreffende Definition zu finden: Durchmustern in der Reihenfolge des Aufschreibens zentraler Unterschied zwischen Sprachen: strikte (ML) und faule (Haskell, Gofer) Auswertung von Argumenten unterschiedlich gestaltete Modulkonzepte parametrische Polymorphie Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 7 ML, Haskell und Gofer Türme von Hanoi move 1 i j k = i ++ move n i j k = (move (n-1) i i ++ "-->" ++ (move (n-1) j "-->" ++ k ++ "\n" k j) ++ k ++ "\n" i k) ++ hanoi 0 = "Ja Ja!" hanoi n = move n "A" "B" "C" Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 8 Konstanten als Listen Integer addr xs [] [] = xs addr [] ys [] = ys addr xs [] "1" = addr xs "1" [] addr [] ys "1" = addr ys "1" [] addr ('0':xs) ('0':ys) []= concat ["0", (addr xs ys [])] addr ('0':xs) ('1':ys) []= concat ["1", (addr xs ys [])] ... addr ('1':xs) ('1':ys) "1" = concat ["1", (addr xs ys "1")] add x y = reverse (addr(reverse x)(reverse y)[]) Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 9 Konstanten als Funktionen höherer Ordnung Boolean wahr t f = t falsch t f = f if-then-else b t f = b t f und a b = if-then-else a (if-then-else b wahr falsch) falsch oder a b = if-then-else a wahr (if-then-else b wahr falsch) nicht a = if-then-else a falsch wahr xoder a b = if-then-else a (if-then-else b falsch wahr) (if-then-else b wahr falsch) Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 10 Konstanten als Funktionen höherer Ordnung Integer bit '0' = falsch bit '1' = wahr tib a = ifthenelse a '1' '0' plus x y z = xoder (xoder x y) z ueber x y z = oder(und x z)(oder(und x y)(und y z)) addr xs [] '0' = xs ... addr [] ys '1' = addr ys "1" '0' addr (x:xs) (y:ys) z = concat [ [(tib(plus (bit x)(bit y)(bit z)))], (addr xs ys (tib(ueber(bit x)(bit y)(bit z))))] add x y=reverse(addr(reverse x)(reverse y)'0') Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 11 Anwendung Funktionen höherer Ordnung Faltung Linksfaltung ( f , x , ( y1 , y2 , ... , yn-1 , yn,) ) = f ( f ( ... f ( f ( x , y1 ) , y2 ) ... ) , yn-1) , yn) foldl f x [] = x foldl f x (y:ys) = foldl f (f x y) ys sum = foldl (+) 0 product = foldl ( ) 1 Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 12 Abstrakte Datentypen Konstruktoren: data X = Konstr_1 | ... | Konstr_n Operationen: op_1:: X-> Typ_1_1->...-> Typ_1_k ... op_m:: X-> Typ_m_1->...-> Typ_m_l Axiome: op_1 = ... op_m = ... Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 13 ADT Keller data IntKeller = EmptyStack | Push (Int, IntKeller) isEmpty :: IntKeller -> Bool top :: IntKeller -> Int pop :: IntKeller -> IntKeller isEmpty EmptyStack = True isEmpty Push (Int, s) = False top Push (i s) = i pop Push (i s) = s Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 14 Polymorphie Parametrische Polymorphie data Stack t = EmptyStack Push (t, Stack t) istLeer :: Stack t -> Bool top :: Stack t -> t pop :: Stack t -> Stack t isEmpty EmptyStack = True isEmpty Push (i, s) = False top Push (i s) = i pop Push (i s) = s Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 15 Typisierung Einsetzen eines Typs in die Typvariable ergibt neuen Typ Muß konsistent sein Typ Stack Int: Push ( 1 Push ( 2 Push ( 3 Empty ))) Typ - Fehler: Push ( 1 Push ( True Push ( 3 Empty ))) Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 16 Dynamisch vs Statisch Typanalyse zur Laufzeit: Berechnet den konkreten Typ jedes Terms Beispiel Lisp. Typanalyse zur Übersetzungszeit: Typinferenz Berechnet den allgemeinsten Typ jedes Terms im gegebenen Typverband Beispiel Haskell, ML, Gofer. Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 17 Prolog Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 18 Entscheidbarkeit Unentscheidbar: Gültigkeit prädikatenlogischer Formeln Erfüllbarkeit prädikatenlogischer Formeln Semi-entscheidbar: Unerfüllbarkeit prädikatenlogischer Formeln Algorithmus terminiert, wenn Formel nicht erfüllbar Resolution, Substitution und Unifikation Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 19 Grundresolution Klauseln K disjunktiv verknüpfte Literal F = ∀x1, ... , xn F* Ki = P1 ∨ P2 ∨ ... ∨ Pm F* = K1 ∧ K2 ∧ ... ∧ Kn F ist unerfüllbar gdw. K1 , K2 , ... , Kt mit Kt ist leere Klausel Ki< t Grundinstanz K ∈ F*, Ki = K[x1/t1][x2/t2] ...[xn/tn] | t1, ... ,tn ∈ D(F) Ki<t aussagenlogische Resolvente von Ka<i , Kb<i Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 20 Problem Grundsubstitution nicht vorausschauend Gierig und daher ineffizient Minimale Substitution für Resolutionsschritt Allgemeiner Unifikator Prädikatenlogische Resolution Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 21 Allgemeinster Unifikator Substitution s ist Unifikator einer Menge L von Literalen { L1, L2, ... ,Lk } wenn L1s = L2s = ... = Lks Allgemeinster Unifikator s falls sub = s sub´ s sub' sub Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 22 Unifikation Eingabe L s:=[] while |Ls| > 1 do Finde in Ls Literale L1, L2 die unterschiedlich in Zeichen x, y if keine Variablen x, y then Ausgabe „Nicht unifizierbar“ else Sei x Variable und y beliebiger Term if x kommt vor in y then Ausgabe „Nicht unifizierbar“ else s:=s[x/y] od Ausgabe s Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 23 Prolog Hornklauselprogramme Tatsachen: T(...). Prozedur: P(...) :- Q1 (...), Q2 (...), ... , Qn(...). Ziel: ?- Q1 (...), Q2 (...), ... , Qn(...). Ergebnis eindeutig? Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 24 Beispiel (1) (2) (3) Frage Q(x,z):-Q(y,z),R(x,y). Q(x,x). R(b,c). ?-Q(x,c) Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 25 Mögliche SLD Resolutionen ?-Q(x,c) (1) (2) ?-Q(y,c),R(x,y) (1) Q(c,c) (2) ?-Q(y,c),R(v,x),R(x,y) ?-R(x,c) (1) (3) (2) ?-R(v,x),R(x,c) Q(b,c) (3) ?-R(c,c) Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 26 Auswertestrategie Deterministisch Tiefensuche Nicht vollständig Endlosschleifen möglich Breitensuche vollständig Kein „occur check“ Prozedurale Auswertung Ausgabe weiterer Lösungen Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 27 Cut Beispiel Hilfs-not Operator not(X) :- X, !, fail. not(_). Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 28 Türme von Hanoi :- use_module(library(lineio)). hanoi(N):- N >=0, move(N,"A","B","C"). move(0,_,_,_) :- !. move(1,A,B,_) :put_chars(A),put_chars(" -> "), put_chars(B), nl, !. move(N,A,B,C) :- is(NN,N-1), move(NN,A,C,B), move(1,A,B,C), move(NN,C,B,A). Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 29 Arithmetik Operatoren pow(1,_,1). pow(_,0,1). pow(B,E,X):-is(EE,E-1), pow(B,EE,Y),mul(B,Y,X). mul(_,0,0):- !. mul(0,_,0). mul(A,B,X):- A < 0,is(AA,-A), mul(AA,B,Y), is(X,-Y). mul(A,B,X):- A > 0,is(AA,A-1), mul(AA,B,Y), is(X,B+Y). Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 30 Flieger :- assert(fz(Fliege)). flugzeug(F):- fz(F). flugzeug(F):put_chars("Ist ein(e)"),write(F),put_chars("ein Flugzeug?"), get_line(L), L=="j",assert(fz(F)). flugzeuge(FS):- findall(F,fz(F),FS). flugzeuge:-flugzeuge(FL),zeige_fz(FL). zeige_fz([]):- put_line("gibt keine!"), !. zeige_fz([F]):- put_chars("Ein(e) "), write(F), put_line("ist ein Flugzeug"),!. zeige_fz([F FS]):-zeige_fz([F]),zeige_fz(FS). ?- flugzeuge. Ein(e) fliege ist ein Flugzeug yes ?- flugzeug(Maus). Ist ein(e) Maus ein Flugzeug? j yes ?- flugzeuge. Ein(e) Fliege ist ein Flugzeug! Ein(e) Maus ist ein Flugzeug! yes ?- flugzeuge(L). = [Fliege,Maus ] Höhere Programmiersprachen SS 2001 Prof. L Dr. Gerhard Goos 31 Datalog: Brücke logisches Programmieren relationale Algebra Einsicht: Fakten p(t1,...,tn) definieren Relation p (im Sinne relationaler Algebra) Voraussetzung: für ti nur Konstante oder Variable erlaubt, also nicht f(t'1,...,t'n) Klauseln q(...) :- q1(...),...,qm(...) definieren Implikation zwischen Relationen Datalog: die Sprache deduktiver Datenbanken: wie Prolog, aber mit Einschränkungen: Fakten und Implikationsklauseln definieren disjunkte Relationen F ∩ P= ∅ Keine Funktionssymbole, nur Variable und Konstante als Argumente in Prädikaten Hornklausel ist logische Beziehung, keine implizite Ablaufsteuerung wie in Prolog alle Klauseln sind sicher: Sichere Klausel: All-Quantifizierung der Variablen begrenzt auf Konstante in Fakten: Variable X heißt begrenzt, wenn sie vorkommt in: benutzerdefiniertem Faktum oder in Form X=a, a konstant, Form X=Y und Y ist begrenzt. Datalog kann wesentlich effizienter verarbeitet werden als Prolog Literatur: J. D. Ullman: Principles of Database and Knowledge-Base Systems. Bd. I, Kap. 3. Computer Science Press 1988 Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 32 Beispiele unbegrenzter Variabler größer als(X,Y) :- X>Y. vater(A,B) :- mann(A) . X,Y unbegrenzt, da ,,X>Y`` eingebaut, nicht benutzerdefiniert. A begrenzt, B unbegrenzt. Intuitive Idee: Aussagen können nur aus Fakten abgeleitet werden und sich nur auf Terme (Variable/Konstante) beziehen, die dort vorkommen; daher Beschränkung auf sichere Fakten. Fakten ohne Funktionssymbole: relationale Algebra Zusätzliche Implikationsklauseln: deduktive Datenbanken Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 33 Wiederholung relationale Algebra gegeben: Relationen R(A,B,C) = { (a,b,c), (d,a,f), (c,b,d) } S(A,B,D) = { (d,a,g), (d,a,c), (c,b,g) } Projektion: π A,B (R) = { (a,b), (d,a), (c,b) } Selektion: σ B=b (R) = { (a,b,c), (c,b,d) } natürlicher Verbund (join): (R , S) = R S = { (d,a,f,g), (d,a,f,c), (c,b,d,g) } Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 34 Reduktion Datalog → relationale Algebra Nichtrekursive Klauseln nicht-rekursiv: Implikation q(...) :- q1(...),...,qm(...) hängt weder direkt noch indirekt von q(...) ab Implikationen qj(...) :- ..., qi(...), ... können so angeordnet werden, daß i < j Konjunktion des Rumpfs ..., qi(...), ... bildet eine Relation r(...), nämlich den natürlichen Verbund q1(...) ... qm(...) des Rumpfs Argumente: die im Rumpf vorkommenden Variablen Wegen Anordnung ist r ohne Rückgriff auf Kopf qj(...) berechenbar Wegen Begrenztheit kommen alle Variablen X in qj(...,X,...) auch in r vor also: Kopf qj(...) ist Relation seiner Variablen und ergibt sich durch Projektion des Rumpfs r auf diese Variablen bei mehreren Rümpfen zum gleichen Kopf: Vereinigung, d.h. Disjunktion der Rümpfe bilden Einsicht: nicht-rekursive Implikation ist zusammengesetzte Operation der Relationenalgebra Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 35 Elimination Umbenennen von Variablen zusätzliche Literale X=Y für gleiche Variablen oder X=c für Konstanten. Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 36 2 Relation der Rümpfe gegeben: Klauseln q1, ... , qm natürlichen Verbund der qi Selektion besonderer Prädikate Vergleichsoperationen für Selektion definiert Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 37 3 Relation der Köpfe Projektion entsprechend der Variablen im Kopf Vereinigung der Relationen der RümpfeBerechne die Relation für jeden Kopf wie bei nichtrekursiven Klauseln Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 38 Beispiel geschwister(X,Y):eltern(X,Z)& eltern(Y,Z)& X/=Y. vetter(X,Y):eltern(X,Z)& eltern(Y,Z')& geschwister(Z,Z'). G(X,Y) := πX,Y(σ X¹Y(E(X,Z) E(Y,Z))) V(X,Y) := πX,Y(E(X,Z) E(Y,Z) G(Z,Z)) Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 39 Rekursive Klauseln Ersetze :- durch =, d.h. jede Implikation setzt sich selbst voraus bedenke: rekursive Implikation q(...) :- q1(...),...,qm(...) nur sinnvoll, wenn es wenigstens eine nicht-rekursive Implikation für das gleiche q(...) gibt daher erfüllt die Lösung des nicht-rekursiven Falls die Gleichung alle weitere Lösungen sind umfangreicher: Halbordnung durch Mengeninklusion Gesamtlösung ist minimaler Fixpunkt des Gleichungssystems Gesamtlösung durch Iteration berechenbar (gleiche Methodik wie bei iterativer Lösung von Datenflußgleichungen) Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 40 Beispiel (fortgesetzt) ... vetter(X,Y):eltern(X,Z)& eltern(Y,Z')& geschwister(Z,Z'). vetter(X,Y):eltern(X,Z)& eltern(Y,Z')& vettern(Z,Z'). V (X,Y) = πX,Y (E (X,Z) E (Y,Z) G (Z,Z)) ∪ πX,Y (E (X,Z) E (Y,Z) V (Z,Z)) Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 41 Auswertungsalgorithmus Initialisiere linke Seiten extensional gegebene Relationen (Fakten) und intensional gegebene Relationen (Implikationen) mit Ø. Berechne rechte Seiten Wiederhole bis Fixpunkt intensionaler Relationen erreicht Wohldefiniert weil: Regeln sicher Operationen ( , ∪ , ... ) monoton bzgl. ⊆ Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 42 Negation Negation, d.h. Übergang zum Mengenkomplement der Relationen in Datalog nicht erlaubt, da dann minimaler Fixpunkt nicht eindeutig: (1) r(1). (2) p(X) :- r(X) & q(X) (3) q(X) :- r(X) & p(X) P=R-Q Q=R-P (S1) (S2) P = ØQ = {1} P = {1} Q= Ø Minimaler Fixpunkt nicht eindeutig trotz geschlossener Welt. Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 43 Einschränkung des Verbots der Negation Geschichtete (stratified) Negationen in sicheren Regeln Kopf P und negiertes Literal ¬S im Rumpf, dann keine transitive Abhängigkeit von P und positivem Literal S. Minimaler Fixpunkt dann eindeutig. Prof. Dr. Gerhard Goos Höhere Programmiersprachen SS 2001 44