Common Lisp Lisp ist Urform aller funktionaler Sprachen Datentypen: Liste (eigentlich Bäume), Atome“ ” Keine Operationen auf Atomen, ausser Vergleich Programme sind symbolische Ausdrücke (S-expressions) repräsentiert als Listen Programme zur Laufzeit modifizierbar Ursprung ist Lisp 1.5: John McCarthy: Recursive Functions of Symbolic Expressions and their Computation by Machine, Part I, Comm. of the ACM 3(1960), Nr. 4, 184-195 Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 128 / 212 Common Lisp Atom Zeichenreihe z ∈ S, spezielle Zeichenreihe nil ∈ S Baum x, y ∈ S: cons[x, y ] = (x.y ) ∈ S Liste (x1 x2 . . . xn ) = (x1 .(x2 .(. . . (xn .nil) . . . ))) x1 x2 xn Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen nil Wintersemester 2005/2006 129 / 212 Grundfunktionen t bezeichnet beliebigen Wert 6= nil, nil = falsch, t = wahr atom : S −→ Bool atom[X ] = t atom[x.y ] = nil eq : Atom × Atom −→ Bool eq[X , X ] = t eq[X , Y ] = nil car : S −→ S car[(x.y )] = x cdr : S −→ S cdr[(x.y )] = y cons : S × S −→ S cons[x, y ] = (x.y ) Alle anderen Fälle liefern nil Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 130 / 212 Grundfunktionen (quote x) abgekürzt ’x: verhindert die Auswertung von x und liefert x zurück car und cdr kombinierbar (cadddr ’(1 2 3 4 5 6 7 8)) = 4 (lambda (x) y) entspricht λx.y Currying mit (lambda (x1 x2 ...) y) (cond (x1,y1), (x2,y2),...,(xn,yn)) entspricht if x1 6= nil then y1 else (cond (x2,y2) ...) Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 131 / 212 Der Sündenfall: Zuweisungen in Lisp Alle heutigen Versionen von Lisp (Emacs Lisp, Scheme, Common Lisp, . . . ) erlauben Zuweisungen: Variablenvereinbarung: (defvar variable) (defvar variable anfangswert) Zuweisung: (set variable wert) Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 132 / 212 Quote & Zuweisungen in Lisp Beispiel (set ’one 1) (set ’two ’one) (set two 2) ; one => 1 ; two => one ; one => 2 Häufig sieht man für (set ’x y) die Kurzform (setq x y) Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 133 / 212 Common Lisp Beispiel Beispiel (defun fact (x) (if (= x 0) 1 (* x (fact (- x 1))))) Daher auch der Beiname: Lots of Irritating Superfluous Parentheses“ ” Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 134 / 212 Lisp Übersicht Älteste funktionale Sprache Moderne Dialekte: Scheme, Common Lisp, Emacs Lisp Dynamisch typisiert Erste Sprache mit automatischer Speicherbereinigung Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 135 / 212 ML (Metalanguage) Als Programmiersprache für einen automatischen Beweiser (LCF) entwickelt 1997 standardisiert ⇒ Standard ML Formal vollständig definiert Viele freie Übersetzer verfügbar Standard ML of New Jersey Poly ML Moscow ML OCaml Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 136 / 212 ML Eigenschaften Funktionen sind first class objects“ ” Strikte Auswertung Starke Typisierung Ausdrucksstarke zusammengesetzte Typen (Tupel, Verbunde) Rekursive Datentypen Typinferenz Parametrische Polymorphie Imperative Konstrukte Modul-System Automatische Speicherbereinigung Ausnahmen Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 137 / 212 ML Wertbindungen val x = 3 bindet x an den Wert 3 x ist ein Name für einen Wert und repräsentiert keinen Speicherplatz Somit kann man x nicht beschreiben, sondern nur einen neuen Wert binden Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 138 / 212 ML Datentypen int Ganze Zahlen uneingeschränkter Länge 123 ~123 real Gleitkommazahlen 0.01 2.7182818 ~1.2E12 7E~5 bool true, false string "Hallo" Verkettung mit ^ Verbunde {a:T1, b:T2, ...} val person = { name = "Hugo", age = 42 } Tupel (1, 2, 3, 4) sind eigentlich Verbunde {1:T, 2:T, ...} #n liefert n-tes Element Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 139 / 212 ML Datentypen Verbunde {a:’a, b:’b, ...} val person = { name = "Hugo", age = 42 } Tupel (1, 2, 3, 4) sind eigentlich Verbunde {1:’a, 2:’b, ...} #n liefert n-tes Element Listen nil Synonym für [] :: entspricht cons bei Lisp nil = [] 9 :: [] = [9] 5 :: [9] = [5, 9] 3 :: [5, 9] = [3, 5, 9] Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 140 / 212 ML Parametrische Polymorphie ML unterstützt parametrische Polymorphie: Beispiel # fun val K # fun val S # fun val I K = S = I = x y = x; fn : ’a -> ’b -> ’a x y z = x z (y z); fn : (’a -> ’b -> ’c) -> (’b -> ’c) -> ’a -> ’c x = S K K x; fn : ’a -> ’a a’, b’, etc. kennzeichnen Typvariablen a’ steht für α, b’ für β, etc. Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 141 / 212 ML Eigene Datentypen Eigene Datentypen durch Angabe der Konstruktoren: Datentyp ::= Konstruktor ::= datatype Bezeichner = Konstruktor [ | Konstruktor ]* Bezeichner [ of Typausdruck ] Beispiel datatype Shape = Clubs | Spades | Hearts | Diamonds datatype Term = | | | Const of int Var of string Add of Term * Term Mul of Term * Term datatype ’a BinTree = Lf | Br of ’a * ’a BinTree * ’a BinTree Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 142 / 212 ML Eigene Datentypen — Beispiele Beispiel val a_term = Plus(Mul(Const(3), Var("x")), Const(4)) val a_tree = Br("Node", Br("Left", Lf, Lf), Br("Right", Lf, Lf)) Achtung Konstruktor-Terme sind keine Funktionsaufrufe! Sie repräsentieren einen Wert des definierten Datentyps Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 143 / 212 ML Funktionen Funktionen sind Werte Sie können Ergebnis einer Berechnung sein fn x => y entspricht λx.y fun f x1 ...xn = y ist Abkürzung für val f = fn x1 => ... => fn xn => y Beispiel # fun muladd z y x = x * y + z val muladd = fn : int -> int -> int -> int # val mul = muladd 0; val mul = fn : int -> int -> int # val erg = mul 2 3; val erg = 6 : int Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 144 / 212 ML Funktionen Vergleich zu imperativen Sprachen In C, Pascal, etc. können auch Funktionen auch Funktionen zurückgeben und als Parameter nehmen (mittels Funktionszeiger) C erlaubt jedoch keine geschachtelten Funktionen, aber Pascal und Modula-3 Was passiert, wenn innere Funktionen auf Variablen der äußeren zugreifen? Z.B. Beispiel val add = fn x => fn y => x + y; val smallinc = add 1 Konzept vom Aufrufkeller nicht ausreichend Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 145 / 212 ML Funktionen val add = fn x => fn y => x + y; x ist frei in fn y => x + y Aufrufschachtel der äußeren Funktion nicht mehr verfügbar Innere Funktion intern (sog. Closure) Werte der freien Variablen × Funktionszeiger Closures werden mittels automatischer Speicherbereinigung entfernt Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 146 / 212 ML Mustervergleich — Pattern Matching Unterstützung, um Konstruktor-Terme auf Passung zu untersuchen Beispiel fun | | | ; diff diff diff diff (Var(s)) (Const(_)) (Plus(a, b)) (Mul(a, b)) = = = = Const 1 Const 0 Plus(diff a, diff b) Plus(Mul(diff a, b), Mul(a, diff b)) Auch als Fallunterscheidung“vorhanden ” Beispiel val s 0 | 1 | n = case x of => "zero" => "one" => if n < 10 then "some" else "too many"; Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 147 / 212 ML Neue Operatoren Ein gültiger Bezeicher besteht entweder Nur aus Buchstaben, Zahlen und (erstes Zeichen muss Buchstabe sein) oder nur aus Sonderzeichen Beispiel fun &&*% x = x + 1; Jede zweistellige Funktion kann infix (sowohl links als auch rechts-assoziativ) verwendet werden Bei infix-Funktionen auch Priorität änderbar! Beispiel infix 4 >>; infixr 8 **; fun x ** y = if y = 0 then 1 else x * (x ** (y - 1)); fun x >> y = x div 2 ** y; Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 148 / 212 ML Funktionale – Higher order functions“ ” Sections fun secl x f y = f(x, y) val secl = fn : ’a -> (’a * ’b -> ’c) -> ’b -> ’c fun secr y f x = f(x, y) Beispiel # fun hallo x = secl "Hallo " op^ x # hallo "Du" Verkettung infix o fun (f o g) x = f (g x) val o = fn : (’b -> ’c) * (’a -> ’b) -> ’a -> ’c Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 149 / 212 ML Funktionale – Higher order functions“ ” map fun map f [] | map f (x::xs) = [] = (f x) :: map f xs; Beispiel val names = ["Horst", "Schorsch", "Peter"]; val greeted_names = map (secl "Hallo " op^) names; foldl fun foldl f e [] | foldl f e (x::xs) = e = foldl f (f(e, x)) xs; Beispiel val sum = foldl op+ 0; sum [1, 2, 3, 4, 5]; Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 150 / 212 ML Faule/strikte Auswertung ML verwendet strikte Auswertung bevor eine Funktion ausgeführt wird, werden alle Argumente berechnet Die eingebauten Konstrukte if B then X else Y, andalso und orelse verwenden jedoch faule Auswertung. if B then X else Y X andalso Y X orelse Y Prof. Dr. Dr. h.c. Gerhard Goos (IPD) ≡ ≡ ≡ (fn true => X | false => Y) (B) if X then Y else false if X then true else Y Höhere Programmiersprachen Wintersemester 2005/2006 151 / 212 Faule Auswertung (call-by-need) Faule Auswertung ist ähnlich zu Namensaufruf (call-by-name) Bei fauler Auswertung jeder Ausdruck höchstens einmal ausgewertet Argumentausdruck wird nicht, wie beim Namensaufruf, in den Rumpf eingesetzt Jedes Auftreten des Arguments wird mit einer Referenz auf den Argumentausdruck versehen Referenzen bilden einen gerichteten Graphen Faule Auswertung eines Ausdrucks entspricht Graph-Reduktion Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 152 / 212 Faule Auswertung Beispiel fun sqr x = x * x sqr(sqr(sqr(2))) sqr × × × sqr ⇒ sqr sqr sqr sqr × 2 2 2 2 ⇒ Prof. Dr. Dr. h.c. Gerhard Goos (IPD) × ⇒ × × ⇒ Höhere Programmiersprachen × × ⇒ 16 256 ⇒ 4 Wintersemester 2005/2006 153 / 212 ML Rekursion Schleifen werden durch Rekursion ausgedrückt Rekursion oft ineffizient, wegen der Kosten für Funktionsaufrufe Beispiel fun fac n = if n = 0 then 1 else n * fac (n - 1); fac(2) wird wie folgt berechnet: ⇒ 2 * fac(2 - 1) = 2 * fac(1) ⇒ 2 * (1 * fac(1 - 1)) = 2 * (1 * fac(0)) ⇒ 2 * (1 * 1) ⇒ 2 * 1 ⇒ 2 Ergebnis eines rekursiven Aufrufs wird zur Berechnung weiter verwendet: n * fac(n -1) fac(2) Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 154 / 212 ML Rekursion — Tail Calls“ ” Beispiel fun fac’ n p = if n = 0 then p else fac’ (n - 1) (n * p) Diese Definition berechnet dasselbe wie fac Ergebnis des rekursiven Aufrufs wird nicht weiterverwendet Rekursiver Aufruf leicht vom Übersetzer entfernbar .L1: cmp je mul sub jmp n, 0 .L2 p = n, p n = n, 1 .L1 .L2: Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 155 / 212 ML Typinferenz ML Programme sind statisch typisiert Programmierer braucht Typen der Variablen (fast) nie anzugeben ML berechnet für jeden Ausdruck einen allgemeinsten Typen ⇒ Typinferenz Literatur: Damas, Milner: Principal type-schemes for functional programs, POPL ’82 Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 156 / 212 ML Modul-Sprache ML erlaubt Modularisierung durch Spezifikation abstrakter Datentypen mit eigenen Namensräumen ML wird häufig in Kernsprache und Modul-Sprache unterteilt Hierbei gelten folgende Analogien Kernsprache Wert ≡ Typ ≡ Funktion ≡ Modul-Sprache Struktur Signatur Funktor Signatur Schnittstellenbeschreibung Struktur Implementierung der Schnittstelle Funktor Parametrisierbare Implementierungen (Funktionen auf Implementierungen) Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 157 / 212 ML Modul-Sprache signature RING = sig type t; val zero : t; val sum : t * t -> t; val prod : t * t -> t; end; Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 158 / 212 ML Modul-Sprache structure Int : RING = struct type t = int; val zero = 0; fun sum(x, y) = x + y; fun prod(x, y) = x * y; end; Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 159 / 212 ML Modul-Sprache functor Matrix(R : RING) : RING = struct type t = R.t list list; fun sum (rowsA, [ ]) = rowsA | sum ([ ], rowsB) = rowsB | sum (rowsA, rowsB) = ListPair.map (ListPair.map R.sum) (rowsA, rowsB); end; Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 160 / 212 ML Imperativ Programmieren Referenz-Typen val p = ref 5; val q = ref 2; p := !p + !q; (* Referenz auf den Wert 5 *) (* Dereferenzieren von q, q und Zuweisung Zuweisung mit := Signatur := = fn : ’a ref * ’a -> unit Achtung Zwei unterschiedliche Referenzen sind nie gleich (bzgl. =) # val p = ref x; # val q = ref x; # p = q (* false *) Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 161 / 212 ML Imperativ Programmieren Sequentielle Anweisungen (E1; E2; ...; En) While-Schleife: while A do B ≡ if A then (B; while A do B) else () Beispiel fun fac(n, res) = let val ip = ref 0 in res := 1; while !ip < n do ( ip := !ip + 1; res := !res * !ip) end; Prof. Dr. Dr. h.c. Gerhard Goos (IPD) Höhere Programmiersprachen Wintersemester 2005/2006 162 / 212