Konzepte der Programmiersprachen Funktionale Programmierung

Werbung
Konzepte der Programmiersprachen
Funktionale Programmierung
Lehrstuhl Prof. Plödereder
Eduard Wiebe
Institut für Softwaretechnologie
Abteilung Programmiersprachen und Übersetzerbau
Sommersemester 2007
Funktionale Programmierung
Grundidee:
I
Beschreibung des Programms durch Denition und
Anwendung von Funktionen (und Funktionalen)
I
Abwendung vom zustandsorientierten, imperativen
(d. h. zuweisungsorientierten) (von-Neumann-)Modell
I
Ausführungsmodell ist im Prinzip der λ-Kalkül
Im Vergleich imperatives (von-Neumann-)Modell:
I
Sequenz von Ausführungsschritten (statements)
I
Variable (Speicherplatz) mit änderbaren Werten
I
Zuweisung
I
Iteration
Funktionales Modell
I
Verknüpfung von Funktionen
I
Funktionsparameter und evtl. Benennung berechneter Werte
I
keine Zuweisung!
I
Rekursion
I
Funktionen als Parameter und Resultat von Funktionen
Funktionen,
λ-Kalkül
Funktion ist eine Abbildung von Denitionsbereich (domain) in
Wertebereich (range)
Beispiel
plus
: Z ×Z →Z
deniert durch:
( , )≡x +y
plus x y
Die Denition der Funktion x + y wird dabei an den Namen
gebunden. x und y sind Funktionsparameter. In
λ-Kalkül-Schreibweise (Präxnotation):
λ x, y. + x y
plus
Funktionen,
λ-Kalkül
(Fortsetzung)
Die im λ-Kalkül dargestellte Funktion ist anonym: Ihr wird kein
Name zugewiesen. Der Punkt ('.') trennt die Parameterliste von der
Funktionsbeschreibung. x und y sind gebundene Variablen.
Auswertung im λ-Kalkül:
(λ x , y . + x y )(2, 3) ≡ +2 3 ≡ 5
Bei der Auswertung werden die Werte (Argumente) textuell für die
Parameter eingesetzt (Substitution).
currying: Rückführung von λ-Ausdrücken mit mehreren gebundenen
Variablen auf λ-Ausdrücke mit genau einer gebundenen Variablen
(nach dem Mathematiker H. B. Curry).
Funktionen als Werte
Beispiel
plus
= λx .λy . + x y ,
dann heiÿt x freie Variable für λy -Ausdruck und der Wert des
Ausdruckes λx ist eine Funktion.
Auswertung:
(λx .λy . + x y )(2)(3) ≡ (λy . + 2 y )(3) ≡ +2 3
Achtung: vor Substitution evtl. konsistente Ersetzung der inneren
gebundenen Variablen:
(λ x .λ y . + x y )(y )(3) 6= (λ y . + y y )(3)
Eigenschaften der funktionalen Programmierung
Vorteile:
I
Funktionen sind Funktionen im mathematischen Sinne
I
keine Seiteneekte (referential transparency)
I
Jede Funktionsanwendung bezeichnet unveränderlich einen
eindeutigen Wert, unabhängig vom Aufruf-Kontext.
Folglich:
I
I
I
I
Kenntnis des globalen Programmzustands für das Verstehen
der Funktion nicht erforderlich.
Anwendung ergibt auf gleichen Parametern gleiches Resultat
können (nicht verschachtelte) Funktionsanwendungen parallel
ausgeführt werden
sind Funktionen beliebig komponierbar (sofern freie Variablen
fest gebunden sind)
I
Programmbeschreibung näher an einer formalen Spezikation
der gewünschten Berechnung und somit leichter verizierbar
I
Programmtransformationen sind relativ einfach durchführbar
Eigenschaften der funktionalen . . . (Fortsetzung)
Nachteile:
I
Eziente Implementierung auf herkömmlichen
Rechnerarchitekturen schwieriger als bei prozeduralen Sprachen
I
für Echtzeit- und maschinennahe Anwendungen daher kaum
geeignet
I
Ausführungsmodell nur für mathematische Probleme
naheliegend
Funktionale Programmiersprachen
I
LISP ( 60)
I
I
∗
Common LISP∗ (statische Namensbindung)
Scheme( 78)∗
I
APL
I
FP (viele Konstruktorformen)
I
ML ( 77)∗ (Typprüfungen, Typinferenzsystem und überladene
Funktionsdenitionen
I
Miranda (Turner 1985)
I
Hope (Burstall 1980)
I
Haskell (Hudak u. w. 90)
sind nicht reine funktionale Sprachen, sondern enthalten auch
imperative Elemente
Grundelemente funktionaler Sprachen
I
Strukturen für Daten-Objekte (meist sehr eingeschränkt)
I
Einige primitive Funktionen: arithmetische/logische
Operationen und Operationen zur Manipulation der
Datenstrukturen
I
Funktionale (functional forms) zur Schaung/Kombination
neuer Funktionen aus existierenden Funktionen
→ spezielle Form höherwertiger Funktionen (higher order
functions): nehmen Funktion(en) als Parameter und/oder
liefern Funktion als Resultat
I
Die Anwendungs-Operation
Programmierung in funktionalen Sprachen:
I
Denition neuer Funktionen mittels Funktionalen
I
Möglichkeit, Funktionen zu benennen, d. h. an Namen zu
binden (zur Wiederverwendung bereits denierter Funktionen)
LISP
John McCarthy (1960): ursprünglich rein funktionale Sprache
Grundideen:
I
Datenobjekte sind entweder
I Atome (Zahlen, Strings, Namen, Funktionen . . . ) oder
I Listen, deren Elemente entweder Listen oder Atome sind
I
Für Atome und Listen stehen primitive Funktionen zur
Verfügung
I
Funktionen sind Datenobjekte! (als Liste deniert)
I
Die primitiven Operationen auf Funktionen erlauben die
Konstruktion neuer Funktionen (durch Komposition) und die
Auswertung von Funktionen.
Mit sehr wenigen Konstrukten kann eine sehr ausdrucksstarke
Programmiersprache konstruiert werden.
LISP Ausführungsmodell
Funktion EVAL wird auf eine Liste angewandt und hat die folgende
Semantik:
(EVAL '(A B C)) ≡
wende (EVAL A) auf Parameter (EVAL B) und (EVAL C) an
(EVAL A) ≡ Wert von A (falls A Name ist)
Primitive Listenoperationen in LISP
I
I
I
I
I
(QUOTE x) ergibt als Wert 'x'. Abkürzung: 'x
(CAR '(A B C)) ergibt A (1. Element der Liste)
(CDR '(A B C)) ergibt (B C) (Liste minus 1. Element)
wegen der Häugkeit dieser Operationen auch
(CAAAR x) ≡ (CAR (CAR (CAR x)))
(CAD..ADR x) ≡ (CAR (CDR ..(CAR (CDR x))..))
(CONS 'A '(B C)) ergibt (A B C)
(CONS '(A B) '(B C)) ergibt ((A B) C D)
(NULL x) ergibt 'T', wenn (EVAL x) '() ergibt '() sonst
'() ist benannt NIL, d. h. (EVAL NIL) ergibt '()
Lisp-Funktionswerte
I
haben die Form (LAMBDA Parameterliste Definition)
I
können (wie alle anderen Werte) benannt werden:
I
(DEFINE '(CDAR (LAMBDA (list)
(CAR (CDR list)))))
Anwendung:
(CDAR '(A B)) ≡ apply (LAMBDA ...) '(A B)
≡ (CAR (CDR '(A B)))
≡ (CAR '(B))
≡ 'B
COND
(COND
( ( bed1
ausdruck1 ))
( ( bed2
ausdruck2 ))
:
( ( bedn
ausdruckn ) ) )
Semantik:
EVAL
(COND . . . )
ergibt
if
( EVAL
bed1 )
= T
dann
( EVAL
ausdruck1 )
e l s i f
( EVAL
bed2 )
= T
dann
( EVAL
ausdruck2 )
dann
( EVAL
ausdruckn )
:
e l s i f
else
( EVAL
bedn )
= T
'()
Beispiel
( DEFINE
'( fac
(LAMBDA
(COND
(n)
( ( EQ
(T
(fac 4) ergibt 24
(
∗
n
0)
n
1)
( fac
(+
n
1))))))))
LISP-Dialekte
I
Hinzufügung weiterer Funktionale (auÿer Komposition),
z. B. MAPCAR (entspricht α in FP)
I
Hinzufügung imperativer, nicht-applikativer Elemente wie
I Zuweisung (SET),
I Sequenz (PROG)
I
und Manipulation von (bestehenden) Datenstrukturen
(RPLACA = replace car)
aus Ezienzgründen
I
statische statt dynamische Namensbindung (Common Lisp)
FP
John Backus (Anfang 70er): rein funktionale Sprache
I
Datenstrukturen bestehen aus Atomen (Zeichenfolge) bzw.
Folgen von Atomen
I
viele Basisfunktionen
(z. B. tail, equals, reverse, length, usw.)
I
Anwendungsoperator ':'
I
xe Menge von Funktionalen zur Erzeugung neuer Funktionen
aus bestehenden basis- bzw. benutzerdenierten Funktionen,
d. h. (basis- und benutzerdenierte) Funktionen können keine
Funktionen als Parameter erhalten bzw. als Resultat liefern.
Gründe: Unbeschränkte Freiheit, Funktionen kombinieren zu
können, führt zu einer unübersichtlichen Programmiersprache
und unübersichtlichen Programmen.
FP (Fortsetzung)
I
Sorgfältige Wahl der Funktionale ermöglicht einfache
Transformationen und Korrektheitsbeweise von
FP-Programmen.
Transformationsregeln können ebenfalls durch FP-Syntax
beschrieben werden (d. h. keine Metasprache erforderlich!).
Beispiele (Funktionale in FP (Auswahl))
1. Komposition: (f ◦ g ) :
x
≡ f : (g : x )
2. Konstruktion: [f1 , f2 , . . . , fn ] :
x
≡ (f1 : x , . . . , fn : x )
3. All-Anwendung: α f : (x1 , . . . , xn ) ≡ (f :
x1
, . . . , f : xn )
Funktionale (Fortsetzung)
Beispiele
4 Bedingte Form:
(IFp f g ) : n ≡ if p :
n
5 While-Iteration:
(WHILE p f ) : x ≡ if p :
= T then f :
n
else g :
n
then (WHILE p f ) : (f : x ) else x
6 Einfügen (vereinfacht, für n > 2)
/f : (x1 , . . . , xn ) ≡ f : (x1 , /f : (x2 , . . . , xn ))
7 Transposition:
trans : ((x11 , . . . , x1n ), . . . , (xn1 , . . . , xnn ))
≡ ((x11 , . . . , xn1 ), . . . , (x1n , . . . , xnn ))
x
Funktionale (Fortsetzung)
Beispiel (Skalarprodukt)
DEF IP ≡ (/+) ◦ (α∗) ◦ trans
Anwendung auf eine Liste ((1, 2, 3), (4, 5, 6))
(/+) ◦ (α∗) ◦ trans : ((1, 2, 3), (4, 5, 6))
= (/+) ◦ (α∗) : ((1, 4), (2, 5), (3, 6))
= (/+) : (4, 10, 18)
= + : (4, + : (10, + : (18)))
= + : (4, + : (10, 18))
= + : (4, 28)
= 32
Delayed Evaluation, Lazy Evaluation
I
I
Am Beispiel COND in LISP sieht man, dass nicht zwangsläug
alle Parameter ausgewertet werden müssen.
Beobachtung zum Prinzip erheben: Parameter müssen erst
ausgewertet werden, wenn ihr Wert benötigt wird
→ lazy evaluation
Vorteile:
I
vermeidet überüssige Auswertung in vielen Fällen
I
ermöglicht Arbeiten mit unendlichen Datenstrukturen
(Miranda, Haskell)
Realisierung: Statt Wert-Eintrag im Datenobjekt, Eintrag der
Funktion, die jeweils im Bedarfsfall den (nächsten) Wert
berechnet.
I
gewährleistet Terminierung des Programms, falls nicht
benötigte Parameter (-Ausdrücke) nicht terminieren würden
I
in Scheme über delay und force kontrollierbar
Herunterladen