Programmiersprachen Sprachen vs. Paradigmen

Werbung
Programmiersprachen
Sprachen vs. Paradigmen
Fragen:
Paradigma:
[griechisch] das, Wissenschaftstheorie: von T.S. Kuhn eingeführter Begriff,
der die Gesamtheit aller eine wissenschaftliche Disziplin in einem Zeitabschnitt beherrschenden Grundauffassungen (z.B. Raum-, Zeitvorstellungen,
methodologische Regeln) bezeichnet und somit festlegt, was als wissenschaftlich befriedigende Lösung angesehen werden kann.
• Seit wann gibt es Programmiersprachen?
...
• Wieviele Programmiersprachen gab bzw. gibt es?
...
(aus: Brockhaus-Wissen, 2004)
• Welche davon muss man als INF, ING-INF, . . . kennen?
...
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 1
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), Programmierparadigmen
2
Programmierparadigmen
• Ein Programmierparadigma ist das einer Programmiersprache . . . oder
Programmiertechnik zugrundeliegende Prinzip.
(vgl. http://de.wikipedia.org/wiki/Programmierparadigma)
’klassische’ Programmierparadigmen:
• imperativ
• funktional
• Unterschiedliche Paradigmen führen zu unterschiedlichen Sichten auf Programmierprobleme und damit oft zu unterschiedlichen Lösungen.
• logisch
• objektorientiert
Anmerkung:
• konkrete Programmiersprachen sind oft Mischformen (hybrid)
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 3
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 4
Programmierparadigmen
Programmierparadigmen
aktuelle Entwicklungen:
Wie eignet man sich die unterschiedlichen Programmierparadigmen am besten an?
• agentenorientierte Programmierung
• aspektorientierte Programmierung
• ...
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 5
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), Programmierparadigmen
6
Programmierparadigmen
Wie eignet man sich die unterschiedlichen Programmierparadigmen am besten an?
Lernziele:
• Denk- und Herangehensweisen
• Kennenlernen von Prinzipien und Konzepten
• adäquate Programmierstile
• Kennenlernen von prototypischen Vertretern, also von konkreten Programmiersprachen, die Prinzipien des Paradigmas besonders klar umsetzen
• konzeptueller Rahmen für Klassifikation, Bewertung und Aneignung konkreter Programmiersprachen
• eigenständiges Lösen von Problemen und Aufgaben
• Vergleiche und Diskussion von Alternativen
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 7
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 8
»Warum funktionale Sprachen studieren?«
»Warum funktionale Sprachen studieren? «
• didaktisches Argument:
verbessertes Verständnis dessen, was Programmieren eigentlich ist
ganz neue Aspekte gegenüber ‘traditionellen’ Sprachen
• praktische Relevanz:
Produktivität der Programmierer wird gesteigert, Softwarekosten werden
gesenkt
(Hinweis: ERLANG von Ericson; XSLT, XQuery )
• theoretische Fundierung:
‘Informatiker zeichnet aus, dass sie das Prinzip des Programmierens und
das Prinzip des Sich-in-Programmiersprachen-Ausdrückens beherrschen’
[Pepper 1998]
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 9
»Klassifikation von Programmiersprachen: «
[E.W.Dijkstra, 1995]
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 10
»Charakteristika (vgl. [Pepper 1998]) «
funktional
Programm ist Ein-/Ausgaberelation;
diese Abbildung wird im Programmtext als Funktion geschrieben
• imperative Programmiersprachen:
– ‘klassische’:
COBOL, FORTRAN, PASCAL, C, ...
– objektorientierte:
SMALLTALK, C++, JAVA, ...
Programme ‘zeit-los’
• deklarative Programmiersprachen:
– logische:
PROLOG, ...
– funktionale:
LISP, SCHEME, ML, MIRANDA, OPAL, GOFER, HASKELL, ...
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), The clarity and economy of expression that the
language of functional programming permits is
often very impressive, and, but for human inertia,
functional programming can be expected to have
a brilliant future.
11
Programme auf abstraktem, math.
orientiertem Niveau
imperativ
Programm ist Arbeitsanweisung für
Maschine; Ein-/Ausgabeverhalten
aus Arbeitsweise der Maschine
analysierbar
‘Zustand’ der ausführenden Maschine beeinflusst Programm
Programme konkret auf Maschinen
bezogen formuliert
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 12
»Haskell «
»Programmiersprache Haskell:«
• benannt nach Haskell B. Curry
Programmierumgebung Hugs
• einer der Pioniere des λ-Kalkül
• Haskell Users Gofer System
• erste Spezifikation der Sprache Ende 80er Jahre
• frei erhältlicher Interpreter
• aktuelle Version: Haskell 98
• für alle gängigen Plattformen
• Download, Tutorials, usw.: http://www.haskell.org/
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 13
»funktionale Programme in Haskell «
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 14
»funktionale Programme in Haskell cont. «
• Definitionen von Funktionen und anderen Werten durch
Gleichungen
• lies ‘::’ als ‘hat Typ’ oder ‘ist vom Typ’
• Beispiele:
• Definition assoziiert Namen (Identifikator) mit Wert eines
bestimmten Typs
size :: Int
size = 12 + 13
• Syntax:
square :: Int -> Int
square n = n * n
<name> :: <type>
<name> = <expression>
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 15
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 16
zwei Arten von Dateistilen:
zwei Arten von Dateistilen: cont
• Skripte (Extension ‘.hs’):
• literate Skripte (Extension ‘.lhs’):
alles ist Programmtext, sofern nicht explizit als Kommentar gekennzeichnet
– Kommentare bis Zeilenende eingeleitet durch zwei aufeinanderfolgende
‘-’
– alles ist Kommentar, sofern nicht am Zeilenanfang durch ‘>’ als
Programmzeile gekennzeichnet
– literat . . . ‘wörtlich’
– Abschnittskommentare zwischen ‘{-’ und ‘-}’
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 17
Beispiel eines Skripts
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 18
Beispiel eines literaten Skripts
{-
Die Berechnung der Funktion Fakultät ist ein
Standardbeispiel fuer Rekursion.
... mehrere Zeilen Kommentartext ...
-}
-- Berechnung der Fakultät mit Konditional if
> fak :: Int -> Int
> fak n = if n == 0 then 1 else n * fak (n - 1)
fak :: Int -> Int
Eine Variante mit Pattern-Matching:
fak n = if n == 0 then 1 else n * fak (n - 1)
> fak 0 = 1
> fak n = n * fak (n - 1)
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 19
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 20
Haskell – Sprachelemente
Haskell – Sprachelemente
• vordefinierte elementare Typen (auch Sorten genannt) für Konstante (=
nullstellige Funktionen):
• Typisierung: jedes Objekt in Haskell hat einen wohldefinierten Typ
• Zweck der Typisierung:
Bool
Int
Char
Float
Integer
Rational
Double
– frühzeitiges Erkennen von Programmierfehlern (type checking)
– schon vor Programmausführung (statische Analyse)
• durch Deklaration mit type lassen sich benutzerdefinierte Typen einführen
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 21
Zur Unterscheidung zwischen Int und Integer
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 22
Typisierung cont.
• Zur Klasse Int gehören ganze Zahlen, die sich mit einer festen Zahl von
Bytes darstellen lassen.
• Typ von Funktionen (auch Funktionalität genannt) :
Definitions- und Wertebereich durch -> getrennt angegeben
• der Wert der Variablen maxBound::Int gibt die grösste als Int darstellbare ganze Zahl an. Dieser Wert ist 2147483647.
• Beispiel:
double :: Int -> Int
double n = 2*n
• Will man beliebig grosse ganze Zahlen verarbeiten, so sollte man den Typ
Integer verwenden.
• bei mehreren Argumenten werden deren Typen durch -> verbunden
• Beispiel: max mit 2 Argumenten aus Int und Wert aus Int
max :: Int -> Int -> Int
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 23
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 24
Typisierung cont.
vordefinierte arithmetische Operatoren
• Interpretation einer Typdeklaration wie
scale :: Picture -> Int -> Picture
• + . . . Summe zweier Zahlen
• * . . . Produkt zweier Zahlen
• erstens: scale hat zwei Argumente: das erste ist vom Typ Picture, das
zweite vom Typ Int
• ˆ . . . Exponentiation: 2 ˆ 3 gibt 8
• zweitens: das Ergebnis der Anwendung von scale ist vom Typ Picture
• - ...
– Differenz, wenn infix verwendet;
– umgekehrtes Vorzeichen bei Präfixverwendung (vgl. negate)
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 25
vordefinierte arithmetische Operatoren cont.
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 26
vordefinierte Vergleichsoperatoren
• div . . . ganzzahlige Division
• für ganze Zahlen, d.h. Typ Int -> Int -> Bool:
>, >=, ==, / =, <=, <
• mod . . . Rest bei ganzzahliger Division (modulo)
• abs . . . Absolutbetrag
• diese Vergleichsoperatoren sind – wie auch die arithmetischen Operatoren
– ‘überladen’ und auch auf Float anwendbar
• negate . . . ändere Vorzeichen
• Typ dann: Float -> Float -> Bool
• für == gilt auch Bool -> Bool -> Bool
bzw. sogar allgemein t -> t -> Bool, sofern für den Typ t Gleichheit
definiert (Hinweis: t hier Typvariable)
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 27
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 28
einige vordefinierte Operatoren bzw. Konstanten für Float
Name(n)
+ - * /
ˆ
**
exp
log
logBase
pi
signum
sqrt
cos, sin, tan
acos, asin, atan
ceiling, floor, round
fromInt
Typ
Float -> Float -> Float
Float -> Int -> Float
Float -> Float -> Float
Float -> Float
Float -> Float
Float -> Float -> Float
Float
Float -> Float
Float -> Float
Float -> Float
Float -> Float
Float -> Int
Int -> Float
Bem.
• werden infix verwendet, d.h. 3 + 4
aber: Verwendung eines Operatorsymbols <op> in Präfixposition möglich
mit Notation (<op>) , d.h. (+) 3 4 == 3 + 4
xn
xy
ex
ln x
loga x
π
• können assoziativ sein; z.B. +, *
• nicht-assoziative Operatoren werden festgelegt als links-assoziativ oder
rechts-assoziativ
z.B. - links-assoziativ, d.h. a - b - c == (a - b) - c
Rundung
Konversion
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), Operatoren
29
Operatoren und Funktionen
• Operatoren haben Bindungsstärke oder Fixität (engl. fixity)
z.B. * hat Fixität 7, + hat 6, ˆ hat 8, daher a + b * c == a + (b * c)
und a ˆ b * c == (a ˆ b) * c
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 30
Konversionen von Operatoren und Funktionen
• Funktionsanwendung hat höchste Bindungsstärke
• allgemeine Schreibweise: Funktionsname vor Argumente(e)
f v1 v2 ...vn
• Beachte: da Funktionsanwendung höhere Bindung als jeder andere Operator wird f n+1 interpretiert als (f n)+1
• werden Infix-Operatoren in Klammern eingeschlossen, so können sie als
Funktionen vor ihren Argumenten verwendet werden
Beispiel:
(+) :: Int -> Int -> Int
Verwendung:
(+) a b == a + b
• Funktionen können zu Operatoren gemacht werden durch Einschluss des
Funktionsnamen in sog. Backquotes
• für andere Interpretation ist explizite Klammerung notwendig: f (n+1)
a ‘max‘ b == max a b
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 31
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 32
Funktionen in Haskell:
Definition von Funktionen
• mit Gleichungen
• Funktionen sind in funktionalen
Programmiersprachen ‘Bürger erster Klasse’ (‘first class citizens’)
• dabei bedingte Ausdrücke verwendbar
• sie unterscheiden sich nicht von Daten,
max :: Int -> Int -> Int
max x y = if x >= y then x else y
• sie können Argumente von Funktionen sein,
• gegebenenfalls verschachtelte bedingte Ausdrücke
• sie können Werte von Funktionen sein,
• sie können Elemente in zusammengesetzten Datenstrukturen sein (z.B.
Listen, Tupel, . . . ).
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 33
fib :: Int -> Int
fib x = if x == 0 then 0
else if x == 1 then 1
else fib (x - 1) + fib (x - 2)
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), Definition von Funktionen cont.
Definition von Funktionen cont.
• Variante mit ‘Wächtern’ (engl. ‘guards’)
• Beispiele cont.:
• boolesche Ausdrücke für Fälle in einer Definition
fib
fib
|
|
|
• idealerweise disjunkt und vollständig abdeckend
• Beispiele:
fib x
| x == 0 || x == 1
| otherwise
= x
= y
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), :: Int -> Int
x
x == 0
= 0
x == 1
= 1
x > 1
= fib (x - 1) + fib (x - 2)
• Variante:
max :: Int -> Int -> Int
max x y
| x >= y
| otherwise
34
35
= x
= fib (x - 1) + fib (x - 2)
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 36
Definition von Funktionen cont.
Definition von Funktionen cont.
• Variante mit ‘Pattern matching’
• Variante mit ‘Pattern matching’
• Beispiel:
fib :: Int -> Int
fib 0 = 0
fib 1 = 1
fib x = fib (x - 1) + fib (x - 2)
mistery :: Int -> Int -> Int
mistery 0 y = y
mistery x y = x
• ‘guards’ bzw. Gleichungen werden sequentiell ausgewertet
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 37
Definition von Funktionen cont.
38
Operationen mit Funktionen
• Verwendung von ‘wildcards’ in Mustern möglich
• Definition von Funktionen
• Anwendung von Funktionen auf Argumente
mistery :: Int -> Int -> Int
– Argumente können verschachtelt wieder Anwendungen von Funktionen
auf Argumente sein
mistery 0 y = y
mistery x _ = x
• ‘wildcards’ immer dann sinnvoll, wenn beliebige Werte im Muster (auf der
linken Seite) zugelassen werden sollen und auf diese auf der rechten Seite
der Gleichung nicht verwiesen werden muss
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 39
• partielle Anwendung von Funktionen
• Funktionskomposition
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 40
Haskell – lexikalische Konventionen
Haskell – lexikalische Konventionen
• Identifikatoren: Bezeichner von Variablen, von Typen von Funktionen usw.
• Identifikatoren müssen in Haskell mit einem Buchstaben beginnen
• dieser kann von einer beliebigen Sequenz von Buchstaben, Ziffern, Unterstrichen ( ) und einzelnen Anführungszeichen gefolgt sein
Konventionen für führende Großbuchstaben bzw. Kleinbuchstaben
• alle Namen, die in Definitionen von Werten verwendet werden (d.h. also
auch alle Funktionsnamen), müssen mit einem kleinen Buchstaben beginnen, ebenso die Bezeichner für Variable und Typvariable
• Beachte: Verwendung von
– einzeln: . . .
– als erstes Zeichen gefolgt von Sequenz: . . .
• mit einem Großbuchstaben beginnen Typnamen (z.B. Int), Konstruktoren, True und False, Modulnamen und auch die Namen von Typklassen
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 41
Haskell – lexikalische Konventionen
42
Haskell – lexikalische Konventionen
kleine Sammlung sog. reservierter Wörter
• Empfehlung: mnemonische, ’sprechende’ Namen verwenden
• können nicht als Identifikatoren benutzt werden
• z.B. zinssatz statt nur z
• dazu zählen:
• Empfehlung: bei Namen, die aus mehreren Worten zusammengesetzt
sind, den Beginn des zweiten Worts (und ggf. folgender Wörter) in Großbuchstaben schreiben
case|class|data|default|deriving|do|else
|if|import|in|infix|infixl|infixr|instance
|let|module|newtype|of|then|type|where|_
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), • z.B. letztesElement
43
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 44
Listen in Haskell:
Primitive Rekursion über Listen
• Listen . . . Zusammenfassung beliebig vieler Elemente vom selben Typ
• auch ‘tail recursion’ genannt
• Darstellung des Listentyp: [<typ>], z.B.
• Definition durch
– Angabe des Ausgangswerts für [] und
– Angabe, wie von Wert für xs auf Wert von (x:xs) übergegangen wird
[Int], [Char], [[Int]], [(Name, Matrnr)], ...
• Listen werden – sofern ungleich der leeren Liste [] - durch Anwendung
des Konstruktors ‘:’ in eindeutiger Weise ausgehend von [] aufgebaut
• Beispiel:
length []
= 0
length (x:xs) = 1 + length xs
• Beispiel: [4, 2, 3] == 4:2:3:[] == 4:(2:(3:[]))
• ‘:’ ist rechts-assoziativ, d.h. x:y:zs = x:(y:zs)
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 45
Primitive Rekursion über Listen
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 46
Rekursion über Listen cont.
Beachte:
• Beispiel: Summe der Werte der Listenelemente
• length ist polymorph, d.h. auf Listen unterschiedlichen Typs anwendbar:
length :: [a] -> Int
• ’wildcard’ in der zweiten Gleichung der Definition möglich, d.h.
length ( :xs) = 1 + length xs
sum :: [Int] -> Int
sum []
= 0
sum (x:xs) = x + sum xs
• Frage: sum [2, 4 .. 11] ֒→ . . . ?
• length ist wie viele andere Funktionen auf Listen im sog. StandardPrelude von Haskell bereits vordefiniert
• sum ist vordefiniert und polymorph
Prelude> :t sum
sum :: Num a => [a] -> a
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 47
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 48
Rekursion über Listen cont.
Der Typ String
Beispiel: Sortieren mit Insertion-Sort
• Spezialfall: Liste aus Zeichen, d.h. type String = [Char]
iSort :: [Int] -> [Int]
• alle polymorphen Listenfunktionen können für String benutzt werden
iSort []
iSort (x:xs)
= []
= ins x (iSort xs)
• Ausgabe mit putStr
putStr :: String -> IO ()
ins :: Int -> [Int] -> [Int]
ins
ins
|
|
• Wechsel zwischen Strings und Werten:
show (2 + 3) ==> "5"
x []
= [x]
x (y:ys)
x<= y
= x:(y:ys)
otherwise = y: ins x ys
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), • umgekehrt:
(read "True") :: Bool ==> True
49
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 50
Darstellung von Listen
Darstellung von Listen
• für Listen von Zahlen, Zeichen und anderen Aufzählungstypen gibt es
Kurzschreibweisen
• Variante: die ‘Differenz’ zwischen dem zweiten und dem ersten Element
ergibt die ‘Schrittweite’
• [n .. m] Kurzform für [n,n+1,...,m]
(falls m kleiner als n ist Liste leer)
• Beispiele:
[3,5 .. 14]
[0.0,0.4 .. 2.0]
[’f’,’h’ .. ’q’]
• Beispiele:
[3 .. 9]
[1.2 .. 4.1]
[’f’ .. ’q’]
= [3,5,7,9,11,13]
= [0.0,0.4,0.8,1.2,1.6,2.0]
= "fhjlnp"
== [3,4,5,6,7,8,9]
== [1.2,2.2,3.2]
== "fghijklmnopq"
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 51
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 52
Listenkomprehension
Listenkomprehension cont.: Beispiele
(vgl. [S.Thompson, 1999, pp.79])
• Vorbild: math. Notation für Mengen
• Beispiel: Teiler(n) = {i∈Nk i≤n, i Teiler von n}
• Sei ex die Liste [2,4,7]
• in Haskell:
teiler :: Int -> [Int]
teiler n = [ i | i<-[1..n], mod n i == 0]
• [ 2*n | n<-ex] ֒→ [4,8,14]
• lies: ‘Nimm alle 2*n für n aus der Liste ex’
– Liste [1..n] wirkt als Generator
– Test mod n i == 0 wählt Elemente aus
• zur Veranschaulichung: Auswertung tabellarisch
n
2*n
• mehrere Tests in Form boolescher Ausdrücke möglich
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 53
=
=
2
4
4
8
7
14
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), Listenkomprehension cont.: Beispiele
54
Listenkomprehension cont.: Beispiele
(vgl. [S.Thompson, 1999, pp.79])
(vgl. [S.Thompson, 1999, pp.79])
• Sei isEven definiert:
• links von <- können nicht nur Variable, sondern auch Muster (pattern)
stehen
isEven :: Int -> Bool
isEven n = (n ‘mod‘ 2 == 0)
• Beispiel: addPairs :: [(Int,Int)] -> [Int]
addPairs pairList = [ n+m | (n,m) <- pairList]
• [ isEven n | n<-ex] ֒→ [True, True, False]
• Veranschaulichung mit Tabelle:
• mehrere Tests möglich
[ n+m
n
m
n+m
• [ 2*n | n<-ex, isEven n, n>3]
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 55
| (n,m) <- [(2,3),(2,1),(7,8)]]
=
2
2
7
=
3
1
8
=
5
3
15
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 56
Listenkomprehension cont.
Listenkomprehension cont.:
(vgl. [S.Thompson, 1999, pp.79])
• Syntax: allgemeine Form ist [ e | q1, ... qk ]
• zusätzlicher Test:
• dabei ist jeder der Qualifikatoren qi
• nur geordnete Paare addieren
addOrdPairs :: [(Int,Int)] -> [Int]
addOrdPairs pairList = [ n+m | (n,m) <- pairList, n < m]
• wichtig: ein Ausdruck lExp bzw. bExp in qi kann auf die in q1 bis qi−1
benutzten Variablen verweisen
• Frage: addOrdPairs [(2,3),(2,1),(7,8)] ֒→ . . . ?
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), – entweder ein Generator der Form p <- lExp mit p Muster und lExp
Ausdruck vom Listentyp
– oder ein Test, d.h. boolescher Ausdruck bExp
57
Listenkomprehension cont.
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), Listenkomprehension cont.:
(vgl. [S.Thompson, 1999, 17.3])
• pythagoräische Tripel: für welche Tripel (x, y, z) ganzer Zahlen gilt:
• mit mehreren Generatoren können Elemente aus zwei oder mehr Listen
kombiniert werden
x2 + y2 = z2 ?
• als Listenkomprehension:
• Beispiel:
pyTriple n = [(x,y,z)| x<-[2 .. n], y<-[x+1 .. n],
z<-[y+1 .. n], x*x+y*y==z*z]
pairs :: [a] -> [b] -> [(a,b)]
pairs xs ys = [ (x,y) | x<-xs , y<-ys ]
• Frage:
pyTriple 100 ֒→ ... ?
pairs [1,2,3] [4,5]
֒→ [(1,4),(1,5),(2,4),(2,5),(3,4),(3,5)]
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 58
59
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 60
Tupel in Haskell:
Tupel in Haskell:
• Aggregationstyp Tupel: auch Tupel dienen – wie Listen – zum Zusammenfassen von Daten zu einem Objekt
• in Tupeln wird eine feste Anzahl von Objekten aggregiert, die aber von
unterschiedlichem Typ sein können
• Unterschied: Liste aggregiert variable Anzahl von Objekten mit identischem Typ
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 61
Verwendung von Tupeln (vgl. [S.Thompson, 1999, 5.2])
– Name (Typ String) und
– Matrikelnummer (Typ Int)
type Student = (String,Int)
• math. Sicht: Tupeltyp als Teilmenge des Kreuzprodukts der Komponententypen
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 62
Funktionen mit Tupeln
• Tupel für zusammengesetzte Resultate von Funktionen; Beispiel:
• meist mit Pattern matching definiert; Beispiel:
minAndMax :: Int -> Int -> (Int, Int)
minAndMax x y
| x>=y
| otherwise
• Beispiel: Studenten seien dargestellt durch Paare aus
addPair :: (Int, Int) -> Int
addPair (x,y) = x+y
= (y,x)
= (x,y)
• Variante mit vordefinierten Selektorfunktionen für Paare
addPair :: (Int, Int) -> Int
addPair p = fst p + snd p
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 63
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 64
Funktionen mit Tupeln
Beispiel: effiziente Berechnung der Fibonacci-Zahlen (vgl.
[S.Thompson, 1999, pp. 75])
• mögliche Definition dieser Selektorfunktionen:
• Idee: Funktion, die zwei aufeinanderfolgende Werte der Folge als Resultat,
d.h. gesucht ist
fst (x,y) = x
snd (x,y) = y
fibPair n = (fib n, fib (n+1))
• in Prelude.hs: mit wildcards in Muster
fst
fst (x,_)
:: (a,b) -> a
= x
snd
snd (_,y)
:: (a,b) -> b
= y
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), • aus einem Paar (u,v) ergibt sich das nachfolgende als (v,u+v) über
fibStep
fibStep :: (Int, Int) -> (Int, Int)
fibStep (u,v) = (v,u+v)
65
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), Beispiel: effiziente Berechnung der Fibonacci-Zahlen cont.
Funktionen mit Tupeln
66
bei Definition von Funktionen auf Tupeln können auch verschachtelte Muster
verwendet werden (vgl. [S.Thompson, 1999, 5.2])
• damit fibPair definierbar als
fibPair :: Int -> (Int, Int)
fibPair n
| n== 0
= (0,1)
| otherwise
= fibStep (fibPair (n-1))
shift :: ((Int,Int),Int) -> (Int,(Int,Int))
shift ((x,y),z) = (x,(y,z))
• schliesslich: das erste Element des Paars ist das Ergebnis
fastFib :: Int -> Int
fastFib = fst . fibPair
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 67
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 68
Funktionale Abstraktion:
Beispiele rekursiv definierter Funktionen cont.
Beispiele rekursiv definierter Funktionen (vgl. [Rabhi & Lapalme, 1999,
1.2.2])
• Summe der natürlichen Zahlen bis n
• Fakultätsfunktion
fact n | n == 0
| n > 0
• ...
sumInt n | n == 0
| n > 0
= 1
= n * fact(n-1)
= 0
= n + sumInt(n-1)
• Summe der Quadrate der natürlichen Zahlen bis n
• ...
sumSqr n | n == 0
| n > 0
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 69
Beispiele . . . cont.
= 0
= n*n + sumSqr(n-1)
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 70
Darstellung dieser Abstraktion in Haskell
das den obigen Beispielen zugrundeliegende Rekursionsprinzip kann wie
folgt abstrahiert werden:
• es wird Funktionswert für die Basis (d.h. für n == 0) definiert
induction base comb n
| n == 0 = base
| n > 0 = comb n (induction base comb (n-1))
Frage: Typ von induction ?
• der Funktionswert für n ergibt sich durch Kombination von n (bzw. eines
aus n berechneten Werts) mit dem Funktionswert für (n-1)
• m.a.W.: der Rekursionsschritt wird als Anwendung einer zweistelligen
Kombinationsfunktion auf n und Funktionswert für (n-1) realisiert
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 71
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 72
Abstraktion: induction
Abstraktion: induction
• induction ist Funktion höherer Ordnung (nimmt eine Funktion als Argument; hier: comb)
• damit Darstellung von fact bzw. sumInt mit Hilfe von induction und
der vordefinierten Funktionen (+) bzw. (*)
> fact n = induction 1 (*) n
> sumInt n = induction 0 (+) n
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 73
Benannte vs. anonyme Funktionen
> fact = induction 1 (*)
> sumInt = induction 0 (+)
• die rechten Seiten dieser Gleichungen zeigen sog. partielle Anwendungen
von mehrstelligen Funktionen
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 74
Benannte vs. anonyme Funktionen
• die Kombinationsfunktion für sumSqr könnte benannt definiert werden,
z.B.
f x y = x*x + y
• dann:
• alternativ: Kombinationsfunktion als sog. anonyme Funktion
• Syntax in Anlehnung an den λ-Kalkül mit (λxy.x ∗ x + y):
sumSqr n = induction 0 (\x y -> x*x + y) n
> sumSqr n = induction 0 f n
bzw.
> sumSqr = induction 0 f
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), • alternativ: Definition auf Funktionsebene möglich, d.h.
bzw.
sumSqr = induction 0 (\x y -> x*x + y)
75
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 76
Funktionen höherer Ordnung:
Die Funktion map
Programmiermuster: Anwendung einer Funktion auf jedes Element einer Liste; Ergebnis ist die Liste der Funktionswerte zu den Elementen
Funktionen sind Funktionen höherer Ordnung, wenn sie
• eine Funktion als Argument nehmen und/oder
mögliche Definitionen:
• eine Funktion als Wert zurückgeben.
• als Listenkomprehension:
Wiederkehrende Programmiermuster (pattern) lassen sich häufig als Funktionen höherer Ordnung abstrahieren.
map f xs = [ f x | x <- xs]
• direkt:
map f []
map f x:xs
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 77
Die Funktion map cont.
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 78
Programmiermuster Filtern:
Wiederkehrende Frage: welche Elemente einer Liste genügen einer gewünschten Eigenschaft?
• Typ von map
map :: (a -> b) -> [a] -> [b]
• Darstellung von Eigenschaften als Prädikate, d.h. als Funktionen vom Typ
t -> Bool für bel. Typ t
• Beispiele:
• Funktion filter nimmt ein Prädikat und eine Liste als Argument und gibt
die Liste derjenigen Elemente zurück, für die das Prädikat zutrifft
doubleAll xs = map double xs
double x = 2 * x
• mögliche Definition als Listenkomprehension:
convertChrs :: [Char] -> [Int]
convertChrs xs = map ord xs
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), = []
= f x : map f xs
filter p xs = [x | x <- xs, p x]
79
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 80
Programmiermuster Filtern:
Elementweises Verbinden von Listen: zip
• mögliche Definition direkt:
• zip: aus Paar von Listen mache Liste mit Paaren korrespondierender
Elemente; ignoriere „überschüssige“ Elemente ohne korrespondierenden
Partner
filter p []
= []
filter p x:xs = if p x then x:filter p xs
else filter p xs
•
• Typ von filter:
• Beispiel:
filter :: ( a -> Bool) -> [a] -> [a]
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), zip :: [a] -> [b] -> [(a,b)]
zip [4,7,1,1] "Koeln"
= [(4,’K’),(7,’o’),(1,’e’),(1,’l’)]
81
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 82
Elementweises Verbinden von Listen: zipWith
Anonyme Funktionen
• Verallgemeinerung zipWith: verknüpfe die korrespondierenden Elemente mit einer zweistelligen Funktion
• Funktionen als Argumente von Funktionen höherer Ordnung können einerseits durch ihren Namen referenziert werden
zipWith f (x:xs) (y:ys) = f x y : zipWith f xs ys
zipWith f
_
_
= []
• wird eine Funktion nur als Argument bei einer Anwendung einer Funktion
höherer Ordnung benötigt (und nirgends sonst), so reicht oft auch eine
sog.anonyme Funktion aus
• Beispiel:
• die Darstellung anonymer Funktionen erfolgt durch einen LambdaAusdruck mit der Syntax: (\<Var(s)> -> <Körper>)
sideBySide p1 p2 = zipWith (++) p1 p2
• Typ? :
zipWith ::
• Beispiele: (\x -> x*x) oder (\x y --> x*x - 2*x*y + y*y)
...................................
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 83
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 84
Funktionen in Prelude.hs
Funktionen in Prelude.hs
Prelude.hs enthält viele nützliche Funktionen
Funktionen, die ein Paar als Argument nehmen, vs. Funktionen mit zwei Argumenten
/usr/local/hugs98-Dec2001/share/hugs/lib/Prelude.hs
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 85
Funktionen in Prelude.hs cont.
id
id
curry
curry f x y
:: ((a,b) -> c) -> (a -> b -> c)
= f (x,y)
uncurry
uncurry f p
:: (a -> b -> c) -> ((a,b) -> c)
= f (fst p) (snd p)
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), Listenfunktionen: Standard list functions (PreludeList)
:: a -> a
= x
head
head (x:_)
:: [a] -> a
= x
const
const k _
:: a -> b -> a
= k
last
last [x]
last (_:xs)
:: [a] -> a
= x
= last xs
(.)
(f . g) x
:: (b -> c) -> (a -> b) -> (a -> c)
= f (g x)
tail
tail (_:xs)
:: [a] -> [a]
= xs
flip
flip f x y
:: (a -> b -> c) -> b -> a -> c
= f y x
init
init [x]
init (x:xs)
:: [a] -> [a]
= []
= x : init xs
x
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 86
87
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 88
Listenfunktionen cont.
null
null []
null (_:_)
:: [a] -> Bool
= True
= False
(++)
[]
++ ys
(x:xs) ++ ys
:: [a] -> [a] -> [a]
= ys
= x : (xs ++ ys)
map
map f xs
:: (a -> b) -> [a] -> [b]
= [ f x | x <- xs ]
filter
filter p xs
:: (a -> Bool) -> [a] -> [a]
= [ x | x <- xs, p x ]
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), Listenfunktionen cont.
concat
concat
:: [[a]] -> [a]
= foldr (++) []
length
length
:: [a] -> Int
= foldl’ (\n _ -> n + 1) 0
(!!)
(x:_)
(_:xs)
(_:_)
[]
89
!!
!!
!!
!!
:: [a] -> Int -> a
0
= x
n | n>0 = xs !! (n-1)
_
= error "Prelude.!!: negative index"
_
= error "Prelude.!!: index too large"
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), Funktionen in Prelude.hs cont.: Faltung
90
Funktionen in Prelude.hs cont.
Fehlererzeugung:
foldl
:: (a -> b -> a) -> a -> [b] -> a
foldl f z []
= z
foldl f z (x:xs) = foldl f (f z x) xs
primitive error
foldr
:: (a -> b -> b) -> b -> [a] -> b
foldr f z []
= z
foldr f z (x:xs) = f x (foldr f z xs)
Beispiel einer Kontrollstruktur:
until
until p f x
...
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), :: String -> a
91
:: (a -> Bool) -> (a -> a) -> a -> a
= if p x then x else until p f (f x)
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 92
Prinzip der strukturellen Induktion für Listen:
Beispiel für Beweis einer Programmeigenschaft
um zu beweisen, dass eine logische Eigenschaft P(xs) für alle endlichen
Listen xs gilt, sind zwei Dinge zu tun
• Induktionsanfang oder -basis:
Beweise P([]) direkt
• Induktionsschritt:
Beweise P(x:xs) unter der Annahme, dass P(xs) gilt
– m.a.W.: es ist zu zeigen, dass
P(xs) ⇒ P(x:xs)
– dabei: P(xs) . . . Induktionshypothese
• Gegeben seien die folgenden Definitionen:
sum :: [Int] -> Int
sum []
= 0
sum (x:xs) = x + sum xs
(sum.1)
(sum.2)
doubleAll :: [Int] -> [Int]
doubleAll []
= []
doubleAll (z:zs) = 2*z : doubleAll zs
(doubleAll.1)
(doubleAll.2)
• Behauptung: für alle endlichen Listen xs ganzer Zahlen gilt:
sum (doubleAll xs) = 2*sum xs
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 93
Beweis einer Programmeigenschaft cont.
• Behauptung:
• Induktionsbasis: Beweise, dass
length (xs ++ ys)
= 2 * sum[]
(base)
• Induktionsschritt: Beweise, dass
sum (doubleAll (x:xs)) = 2 * sum (x:xs)
(ind)
=
length xs + length ys
• Dabei gelten die folgenden Definitionen:
length []
length (z:zs)
= 0
= 1 + length zs
[]
++ zs = zs
(w:ws) ++ zs = w : (ws ++ zs)
unter der Induktionshypothese
sum (doubleAll xs)
94
weitere Beispiele für Induktionsbeweise
Struktur des Beweises:
sum (doubleAll [])
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), =
2 * sum xs
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), (length.1)
(length.2)
(++.1)
(++.2)
(hyp)
95
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 96
weitere Induktionsbeweise cont.
weitere Induktionsbeweise cont.
Struktur des Beweises:
• Sei reverse definiert durch:
• Induktionsbasis: Beweise, dass
length ([] ++ ys) = length [] + length ys
(base)
reverse []
reverse (z:zs)
= []
= reverse zs ++ [z]
(reverse.1)
(reverse.2)
• Behauptung: für alle endlichen Listen xs und ys gilt
• Induktionsschritt: Beweise, dass
length ((x:xs) ++ ys) = length (x:xs) + length ys (ind)
reverse (xs ++ ys) = reverse ys ++ reverse xs
(reverse++)
unter der Induktionshypothese
length (xs ++ ys)
=
length xs + length ys
(hyp)
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 97
weitere Induktionsbeweise cont.
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 98
Ausführung des Beweises
Struktur des Beweises für Beziehung (reverse++):
• Induktionsbasis: Beweise, dass
....................................................
• Induktionsschritt: Beweise, dass
....................................................
unter der Induktionshypothese
....................................................
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 99
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 100
Funktionen höherer Ordnung:
Falten einer Funktion f cont.
Falten einer Funktion f in eine nichtleere Liste
In Haskell:
Fälle:
foldr1 f [x]
• Falten von f in eine einelementige Liste [x] ergibt x
foldr1 f (x:xs) = f x (foldr1 f xs)
= x
Typ:
• Falten von f in eine längere Liste entspricht
foldr1 :: (a -> a -> a) -> [a] -> a
foldr1 f [e1, e2, ... , ek]
= e1 ‘f‘ (e2 ‘f‘ ( ... ‘f‘ ek) ... )
= e1 ‘f‘ (foldr1 f [e2, ... , ek])
= f e1 (foldr1 f [e2, ... , ek])
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 101
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), Falten einer Funktion f cont.
Falten einer Funktion f cont.
Beispiele:
foldr1 (+) [4,7,1,1]
= 13
foldr1 (||) [False, True, False]
= True
Verallgemeinerung:
durch Angabe eines Werts s für die leere Liste lässt sich eine für alle endlichen Listen anwendbare Faltungsfunktion definieren
foldr1 (++) ["Dies ", "ist ", "ein ", "Beispiel!"]
= "Dies ist ein Beispiel!"
foldr1 max [4,7,1,1]
= 7
foldr1 min [4,7,1,1]
= 1
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 102
103
foldr f s []
= s
foldr f s x:xs
= f x (foldr f s xs)
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 104
Falten einer Funktion f cont.
Beispiele für mit foldr definierbare Standardfunktionen
von Haskell
Typ von foldr:
concat :: [[a]] -> [a]
concat xs = foldr (++) [] xs
foldr :: (a -> a -> a) -> a -> [a] -> a
and :: [Bool] -> Bool
and bs = foldr (&&) True bs
falls Startwert von Typ b:
foldr :: (a -> b -> b) -> b -> [a] -> b
rev :: [a] -> [a]
rev xs = foldr snoc [] xs
snoc :: a -> [a] -> [a]
snoc x xs = xs ++ [x]
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 105
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), foldr1 vs. foldr
Beispiele . . . cont.
Frage: Lässt sich foldr1 mit foldr definieren? Wenn ja, wie und unter
welchen Bedingungen an f?
iSort :: [Int] -> [Int]
iSort xs = foldr ins [] xs
ins
ins
|
|
106
foldr1
x []
= [x]
x (y:ys)
x<= y
= x:(y:ys)
otherwise = y: ins x ys
...................................
...................................
...................................
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 107
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 108
Funktionen als Werte:
Funktionen als Werte:
Ein ungewohnter Gedanke:
. . . mit weitreichenden Konsequenzen:
. . . I was extremely ill-equipped to appreciate functional programming when I
encountered it: I was, for instance, totally baffled by the shocking suggestion
that the value of a function could be another function.
. . . Die Möglichkeit, Funktionen auf vielfältige Weise mittels anderer Funktionen miteinander zu verknüpfen, eröffnet Programmiermethoden, die in ihrer
Eleganz und Ausdrucksstärke weit über das hinausgehen, was uns aus der
traditionellen imperativen Programmierung vertraut ist. Man kann mit Fug und
Recht sagen, dass erst diese Techniken die funktionale Programmierung zu
dem machen, was sie ist: eine extrem elegante Form, Algorithmen auszudrücken.
[E.W. Dijkstra; zitiert in: Pepper 1999]
[Pepper 1999, p.89]
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 109
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 110
Aber:
Daher:
. . . Dieser Programmierstil ist für viele ungewohnt, und weil Unvertrautes erst
einmal verunsichert, ist die erste Reaktion häufig Ablehnung. . . .
Und erhöhte Abstraktion geht grundsätzlich mit erhöhter intellektueller
Herausforderung einher. Etwas sarkastisch ausgedrückt: Wo man sich in C
oder BASIC mit fleißigem Testen und Debugging irgendwie zum Ziel durchwursteln kann, muss man hier nachdenken. Und erstaunlicherweise findet
manch einer Letzteres abschreckender als Ersteres.
. . . ein objektives Problem: Auf dieser Ebene (mehr oder weniger) virtuos
mit Funktionen zu spielen, erhöht zweifellos den Abstraktionsgrad, auf dem
Problemlösen stattfindet.
[Pepper 1999, p.90]
[Pepper 1999, p.90]
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 111
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 112
Funktionen als Werte:
Funktionen als Werte: cont.
mirror :: (Float -> Float) -> (Float -> Float)
Beispiel: Manipulation beliebiger reeller Funktionen
(vgl. [Pepper 1999, 8.2.2])
mirror f x = f (-x)
Sei f :: Float -> Float beliebig.
stretch :: Float -> (Float -> Float) -> (Float -> Float)
Dann:
stretch r f x = f (x/r)
shift :: Float -> (Float -> Float) -> (Float -> Float)
Fragen:
shift dx f x = f (x - dx)
• Wie wirken sich shift, mirror und stretch aus?
...
• Wie lassen sie sich als Funktionskomposition darstellen?
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 113
Funktionskomposition
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 114
Funktionskomposition: Variante Vorwärtskomposition
Verknüpfung von Funktionen als Kontrollstruktur:
f . g bedeutet: wende zuerst g, dann f an;
m.a.W. Verknüpfung muss von rechts nach links gelesen werden als
‘g, dann f’
Ausgabe einer Funktion wird Eingabe der nachfolgenden
Definition:
Definition eines Operators >.>, der Funktionen in der Reihenfolge von links
nach rechts verknüpft:
(f . g) x = f ( g x )
Typ von ‘.‘:
infixl 9 >.>
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(>.>) :: (a -> b) -> (b -> c) -> (a -> c)
Funktionskomposition ist assoziativ, d.h. für alle f, g und h
g >.> f = f . g
f . (g . h) = (f . g) . h
z.B. bei Pictures.hs: rotate = flipH >.> flipV
in Haskell aus technischen Gründen als rechtsassoziativ behandelt
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 115
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 116
Funktionskomposition: eine Funktion zweimal anwenden
Funktionskomposition
Verallgemeinerung: n-fach wiederholte Funktionsanwendung
twice :: (a -> a) -> (a -> a)
twice f = f . f
iter :: Int -> (a -> a) -> (a -> a)
iter n f
| n > 0
= f . iter (n-1) f
|otherwise = id
Sei
succ :: Int -> Int
succ n = n + 1
Frage: Was ergibt
iter n double 1 ==> ... ?
Was ergibt dann
(twice succ) 7 ==> ... ?
[Bem.: Variante: Mit Faltung definierbar als
iter n f = foldr (.) id (replicate n f)
]
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 117
Partielle Anwendung
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 118
Partielle Anwendung cont.
Beispiel:
zwei partielle Anwendungen:
multiply :: Int -> Int -> Int
multiply x y = x * y
• multiply 2 :: Int -> Int
• map (multiply 2) :: [Int] -> [Int]
Frage: was ergibt
multiply 2 ==> ... ?
Variante ohne partielle Anwendung von map:
doubleAll xs = map (multiply 2) xs
Beispiel:
doubleAll :: [Int] -> [Int]
doubleAll = map (multiply 2)
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 119
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 120
Bestimmung des Typs einer partiellen Anwendung
Assoziativität
Streichungsregel (cancellation rule):
Wenn der Typ einer Funktion f
t1 -> t2 -> ...tn -> t
ist
und diese wird angewendet auf
e1::t1 , e2::t2 , ..., ek ::tk mit (k<=n),
Beachte:
dann ergibt sich der Ergebnistyp durch „Streichen“ der Typen t1 bis tk ,
• -> ist rechtsassoziativ, d.h. a -> b -> c entspricht a -> (b -> c)
d.h. der Ergebnistyp von f e1 e2 ...ek ist
• -> ist nicht assoziativ; Beispiel:
• Funktionsanwendung ist linksassoziativ, d.h.
f x y = (f x) y
f x y /= f (x y)
tk+1 -> tk+2 -> ...tn -> t
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), g :: (Int -> Int) -> Int
g h = (h 0) + (h 1)
121
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), Funktionen in Haskell
Funktionen in Haskell
Beachte: jede Funktion in Haskell nimmt exakt ein Argument
Allgemein:
f e1 e2 ...ek
bzw.
t1 -> t2 -> ...tn -> t
So bedeutet
multiply :: Int -> Int -> Int
wegen der Rechtsassoziativität
multiply :: Int -> (Int -> Int)
Damit
multiply 2 :: Int -> Int
und
(multiply 2) 5 :: Int
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 122
sind Abkürzungen für
(...((f e1) e2 ) ...ek )
bzw.
t1 -> (t2 -> (...(tn -> t) ...))
123
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 124
Schlussfolgern über Programme:
Schlussfolgern über Programme
Beweise auf Funktionsebene (vgl. [S.Thompson, 1999, 10.9])
• Prinzip der Extensionalität:
zwei Funktionen f und g sind gleich, wenn sie für jedes Argument jeweils
den selben Wert haben
Beispiel:
f . id = f
(compId)
• Kontrast: Prinzip der Intensionalität:
zwei Funktionen f und g sind nur gleich, wenn sie die selben Definitionen
haben
Beweis: für bel. Argument x gilt
(f . id) x
= f (id x)
= f x
(Def. . )
(Def. id)
m.a.W. von ihrer Wirkung her sind die Funktionen f . id und f für alle x
identisch
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 125
Schlussfolgern über Programme: Verküpfung zwischen
map und Funktionskomposition
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 126
Beispiel cont.
• Induktionsbasis: Beweise, dass
map (f.g) [] = (map f . map g) []
• Behauptung: für alle endlichen Listen xs gilt:
(base)
• Induktionsschritt: Beweise, dass
map (f.g) xs = (map f . map g) xs
(map.3)
map (f.g) (x:xs) = (map f . map g) (x:xs)
unter der Induktionshypothese
Beweis: strukturelle Induktion
map (f.g) xs = (map f . map g) xs
• verfügbare Gleichungen:
(hyp)
• Durchführung des Beweises:
map f []
= []
map f (x:xs) = f x : map f xs
(map.1)
(map.2)
(f.g) x
(comp.1)
= f (g x)
(ind)
................................................................
................................................................
................................................................
• ...
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 127
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 128
weiteres Beispiel: Programmtransformation
Beweistechnik: Gegenbeispiel
• Behauptung: filter p . map f = map f . filter (p.f)
• in [S.Thompson, 1999, p. 162] findet sich – ohne Bedingungen an die Funktion f zu
formulieren – die folgende Behauptung:
. . . We can also define foldr1 from foldr, thus
• Beispiel für Anwendung:
filter (0<) . map (+1)
= map (+1) . filter ((0<).(+1))
= map (+1) . filter ((0<=))
foldr1 f (x:xs) = foldr f x xs
• Diese Behauptung erscheint zunächst plausibel, denn es gilt u.a.:
Prelude>
6
Prelude>
6
Prelude>
120
Prelude>
120
Beweis durch strukturelle Induktion:
• Erinnerung:
filter p []
= []
filter p (x:xs)
| p x
= x:filter p xs
| otherwise =
filter p xs
(filter.1)
(filter.2)
(filter.3)
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 129
Beweistechnik: Gegenbeispiel
foldr1 (+) [1,2,3]
foldr (+) 1 [2,3]
foldr1 (*) [1 .. 5]
foldr (*) 1 [2 .. 5]
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 130
Beispiel: foldr1 und foldr
Gegenbeispiele machen deutlich, dass (foldr1.0) zumindest nicht für alle
(beliebigen) Funktionen f gilt:
Prelude>
"abcd"
Prelude>
"bcda"
Prelude>
8
Prelude>
9
(foldr1.0)
• Behauptung:
für alle endlichen Listen xs und assoziative und kommutative Funktionen f gilt:
foldr1 f (x:xs)
foldr1 (++) ["a","b","c","d"]
= foldr f x xs
• Struktur des Beweises:
• Induktionsbasis: Beweise, dass
foldr (++) "a" ["b","c","d"]
....................................................
foldr1 (^) [2,3]
foldr
• Induktionsschritt: Beweise, dass
(^) 2 [3]
....................................................
unter der Induktionshypothese
....................................................
Frage: Welche Forderungen muss f erfüllen, damit (foldr1.0) gilt?
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 131
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 132
Einführung in Prolog:
Einführung in Prolog:
Prolog . . . Programming in Logic (s.a. [M.L.Scott, 2000, 11.3])
• Axiome werden in Standard-Notation, als sog. Horn-Klauseln geschrieben
• Programmierer kann eine Kollektion von Axiomen angeben, aus denen
Theoreme abgeleitet werden können
• Benutzer gibt Ziel (goal), d.h. zu beweisendes Theorem, vor und die Implementierung versucht, mit Axiomen und Inferenzschritten das Ziel zu beweisen
• zu Inferenzschritten gehört auch Auswahl von Werten für Variable
• Horn-Klauseln bestehen aus
– Kopf (head) oder Konsequenz H
– Körper (body) aus Termen Bi
H ← B1, B2, ..., Bn
– Semantik: wenn alle Bi wahr, dann H ebenfalls wahr
– lies: H, wenn B1, B2, ... und Bn
• Beachte: nicht alle logischen Aussagen lassen sich als Hornklauseln
schreiben
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 133
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), Einführung in Prolog:
134
Einführung in Prolog:
• Kern der Verarbeitung ist Schlussfolgern mit Resolution
• bei der Resolution können freie Variable durch Unifikation mit Ausdrücken
in übereinstimmenden Termen Werte bekommen
• Beispiel:
• Beispiel:
C ← A, B
D ←C
mortal(X) ← human(X)
human(sokrates)
D ← A, B
mortal(sokrates)
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 135
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 136
Einführung in Prolog:
Einführung in Prolog: Elementare Syntax
Analogie:
• Klauseln aus Termen zusammengesetzt
• Interpreter funktionaler Sprachen bewertet Funktionen in einer Umgebung
mit anderen definierten Funktionen und Konstanten
• Prolog: Interpreter arbeitet im Kontext einer Datenbasis von Klauseln
(Horn-Klauseln), die als wahr gelten
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 137
Einführung in Prolog: Elementare Syntax
• Konstante ist entweder Atom oder Zahl
• Struktur ist entweder ein logisches Prädikat oder eine Datenstruktur
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 138
Syntax: Variable
• Atome . . . ähneln Symbolen in Lisp
• lexikalisch: Identifikator beginnend mit Grossbuchstaben
z.B.
Kopf Kontostand X Y3
• lexikalische Konventionen:
– wenn Identifikator, dann beginnend mit Kleinbuchstaben; z.B.
hans uni magdeburg
– Zeichenketten in Anführungszeichen (quotes): z.B.
’Hallo, Welt’
– spezielle Zeichen:+ *
• Skopus einer Variable ist immer die Klausel, in der sie auftaucht
• keine Typendeklaration; Typüberprüfung nur zur Laufzeit, wenn spezielle
Verwendung versucht wird (vgl. Lisp)
• Variable können durch Unifikation instantiiert werden (d.h. einen Wert annehmen)
• Zahlen . . . wie in anderen Programmiersprachen
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), • Terme können Konstante, Variable oder Strukturen sein
139
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 140
Fakten vs. Regeln
Syntax: Strukturen
• aus Funktor und Liste von Argumenten, die bel. Terme, d.h. Atome oder
Variable oder Strukturen, sind
• Klauseln lassen sich unterscheiden in Fakten und Regeln
• Fakten . . . Horn-Klauseln ohne rechte Seite
• Beispiele:
happy(doris).
married(gerhard, doris)
bundeskanzler(gerhard)
bintree(a, bintree(b,c))
married(doris, gerhard).
bundeskanzler(gerhard).
• Beachte: ’(’ unmittelbar, ohne Leerzeichen, an Funktor anschliessend
(rechte Seite ist ’leer’, d.h. linke Seite gilt ohne Voraussetzungen)
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 141
Fakten vs. Regeln
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 142
Listen in Prolog
• Regeln . . . Horn-Klauseln mit rechter Seite aus konjunktiv verknüpften Termen
• da Listen oft verwendet werden, gibt es dafür bequeme Darstellungen
• Beispiel: Liste mit (vier festen) Personennamen
happy(Woman) :- married(Woman, Man), vip(Man).
[max, paula, susi, tom]
vip(Person) :- bundeskanzler(Person).
• Listen können auch Variable enthalten für Elemente, die noch bestimmt
werden müssen
vip(Person) :- president(Person).
– Beispiel: Liste mit vier noch unbekannten Personennamen
[P1, P2, P3, P4]
– Beispiel: Liste mit (zwei) bekannten und (zwei) noch offenen Namen
[max, FreundinVonMax, susi, SusisFreund]
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 143
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 144
Listen in Prolog
Listen in Prolog
• eine spezielle Notation erlaubt, Listen mit variablen Listenresten darzustellen
• die Notation für variable Listenresten erlaubt allgemeinere Darstellungen
– Syntax: [Kopf | Rest]
– Semantik: die dargestellte Struktur ist unifizierbar mit einer Liste mit einem bel. ersten Element und einer (evtl. leeren) Restliste bel. Länge
– Beispiele:
[max, paula, susi, tom]
Kopf = max
Rest = [paula, susi, tom]
[max]
Kopf = max
Rest = []
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 145
Listen in Prolog
– Syntax: [Elem1, Elem2, ... , ElemN | Rest]
– Semantik: die dargestellte Struktur ist unifizierbar mit einer Liste mit
N Elementen, die jeweils mit den Variablen oder Konstanten Elem1,
Elem2, ... , ElemN unifizieren, und einer (evtl. leeren) Restliste bel. Länge
– Beispiele:
∗ eine Liste mit mind. drei bekannten und evtl. weiteren Elementen
[max, paula, susi | Weitere]
∗ eine Liste mit mind. vier und evtl. weiteren Elementen
[Elem1, Elem2, Elem3, Elem4 | Rest]
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 146
Listen in Prolog: Relation append
• Programmieren in Prolog erfordert ‘Denken in Relationen’
• Fakt: falls List1 leer, dann liegt Relation append für beliebige Listen
List2 vor mit List3 unifiziert mit List2
• Beispiel: Aneinanderhängen von Listen
append([], List, List).
• statt wie in funktionalen Sprachen eine (zweistellige) Funktion zum Aneinanderhängen von Listen zu entwerfen, fragt man sich beim ‘Denken in
Relationen’: Wann liegt zwischen drei beliebigen endlichen Listen List1,
List2 und List3 die (dreistellige) Relation append vor?
• die Relation append soll dabei genau dann zwischen List1, List2 und
List3 vorliegen, wenn List3 sich durch Aneinanderhängen von List1
und List2 ergibt
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 147
• Regel: falls List1 nicht leer (d.h. setzt sich aus Kopf H und bel. Rest R1
zusammen), dann liegt Relation append zwischen List1(= [H1|R1]),
einer beliebigen Liste List2 und einer aus H1 als Kopf und Z als Rest
gebildeten Liste vor, wenn append zwischen R1, List2 und Z vorliegt
append([H1|R1], List2, [H1|Z]) :- append(R1, List2, Z).
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 148
Prolog: Generieren mehrfacher Lösungen
Prolog: Generieren mehrfacher Lösungen cont.
(vgl. Clocksin u. Mellish, Ch. 4.1)
• wenn Prolog Lösungen auf unterschiedlichem Wege bestimmt, gelten sie
als unterschiedlich
• Reihenfolge bei der Abarbeitung der Teilziele der rechten Seite einer Klausel:
von links nach rechts
?- possible_pair(X,Y).
X = john, Y = susi ;
X = john, Y = mary ;
...
• Beispiel
possible_pair(X,Y) :- boy(X),girl(Y).
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 149
Generieren mehrfacher Lösungen cont.
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 150
Generieren mehrfacher Lösungen cont.
• unendlich viele Lösungen
• alternative Lösungen
is_integer(0).
• z.B. bei Zielen mit vielen uninstantiierten Variablen
is_integer(X) :- is_integer(Y), X is Y+1.
member(X, [X|_]).
member(X, [_|Y]) :- member(X,Y).
• Einschub: Prädikat is
– wird in Infix-Notation verwendet: Var is Term
– zum Zeitpunkt der Auswertung müssen Variable auf der rechten Seite
so instantiiert sein, dass der Term auf der rechten Seite ausgewertet
werden kann
– die Variable auf der linken Seite wird mit dem errechneten Wert zu unifizieren versucht
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), possible_pair(X,Y) :- boy(X),girl(Y).
boy(john).
boy(paul).
...
girl(susi).
girl(mary).
...
151
?- member(a, X).
...
?- member(a, [a,b,r,a,c,a,d,a,b,r,a]).
/* 5 mal erfolgreich */
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 152
Verarbeitung eines Ziels in einer Prolog-Datenbasis
(skizziert)
Der ‘Cut’
(vgl. Clocksin u. Mellish, Ch. 4.2)
• von oben nach unten
• ein Ziel kann entweder mit einem Fakt oder mit dem Kopf einer Regel unifiziert werden;
dabei werden ggf. Variablen instantiiert
• diese Stelle wird in Datenbasis markiert
• bei einer Regel müssen dann die Teilziele der rechten Seite erfüllbar sein
• falls dies fehlschlägt (oder wenn weitere Lösungen verlangt) kann eine alternative Klausel
für das Ziel gesucht werden (Backtracking)
• dazu: die Instantiierungen aus der letzten Unifikation mit dem Ziel werden aufgehoben
und die Suche geht an der markierten Stelle weiter
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 153
• spezieller Mechanismus, um in Prolog-Programmen auszudrücken, welche Möglichkeiten beim Backtracking nicht mehr betrachtet werden müssen
• syntaktisch: !
d.h. Ziel mit Prädikat ! (sprich: Cut) ohne Argumente
• semantisch: wenn ein sog. Cut angetroffen wird, dann ist das System danach festgelegt auf alle Auswahlen, die getroffen wurden, seit das Elternziel (d.h. der Kopf der aktuell bearbeiteten Regel) invoziert wurde. Alle Alternativen für das Elternziel und die Teilziele bis zum Cut werden entfernt
und nicht weiterverfolgt.
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), Typische Verwendungen des ‘Cut’
Beispiel: Bestätigen einer Regelauswahl
(vgl. Clocksin u. Mellish, Ch. 4.3)
•
• Bestätigen einer Regelauswahl
Wenn Du soweit gekommen bist, hast Du die richtige Regel für das Ziel
ausgewählt.
• Fehlschlagenlassen eines Ziels ohne Suche nach alternativen Lösungen
Wenn Du soweit gekommen bist, solltest Du aufhören, dieses Ziel zu verfolgen.
• Beenden des Erzeugens alternativer Lösungen durch Backtracking
Wenn Du soweit gekommen bist, hast Du die Lösung bzw. alle Lösungen
gefunden, und es gibt keinen Grund, nach weiteren Alternativen zu suchen.
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 154
155
sum_to(N,X) ... wenn N ganze Zahl, dann X die Summe der Zahlen
von 1 bis N
sum_to(1,1) :- !.
sum_to(N,R) :- N1 is N-1, sum_to(N1,R1), R is R1+N.
• für N mit 1 instantiiert würde auch die zweite Klausel zutreffen
• Cut drückt in erster Klausel aus, dass dies die einzige anzuwendende Regel ist für den Fall N mit 1 instantiiert
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 156
Beispiel: Bestätigen einer Regelauswahl
Die Kombination ‘cut-fail’
•
• Variante:
sum_to(N,1) :- N=<1, !.
• wird vor ein fail ein Cut eingebaut (sog. ‘cut-fail’-Kombination), so wird
Backtracking verhindert und keine Alternative gesucht
sum_to(N,R) :- N1 is N-1, sum_to(N1,R1), R is R1+N.
• hier wird der Fehler unendlicher Berechnung für N gleich oder kleiner Null
vermieden
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), fail ... eingebautes nullstelliges Prädikat: schlägt fehl und verursacht
Backtracking
157
Cut zum Beenden eines ‘Generiere und Teste’
• Beispiel:
average_taxpayer(X) :- foreigner(X),!,fail.
average_taxpayer(X) :- spouse(X,Y),
gross_income(Y,Inc),
Inc > 3000,!,fail.
average_taxpayer(X) :- ...
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 158
Die Kombination ‘cut-fail’
• Beispiel: ganzzahlige Divison könnte wie folgt implementiert werden
• mögliche Darstellung von not:
divide(N1, N2, Result) :is_integer(Result),
Product1 is Result*N2,
Product2 is (Result+1)*N2,
Product1 =< N1, Product2 > N1,
!.
not(P) :- call(P),!, fail.
not(P).
• da das Ergebnis eindeutig ist, kann mit dem Cut der vergebliche Versuch
verhindert werden, weitere Lösungen zu finden
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 159
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 160
Prolog: Entwickeln von Programmen
Prolog: Entwickeln von Programmen
• Beispiel: eine Relation rev soll entwickelt werden, die zwischen zwei
Listen genau dann vorliegt, wenn die eine Liste genau die Elemente der
anderen Liste enthält, aber in umgedrehter Reihenfolge
• m.a.W. rev soll sich wie folgt verhalten:
9 ?- rev([1,b,c],X).
• erster Schritt: Aufstellen einiger elementarer Fälle, also hier z.B.
rev([],[]).
rev([X],[X]).
rev([X,Y],[Y,X]).
rev([X,Y,Z],[Z,Y,X]).
...
X = [c, b, 1]
Yes
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 161
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 162
Prolog: Entwickeln von Programmen
Prolog: Entwickeln von Programmen
• zweiter Schritt: Regel für den allgemeinen Fall ableiten, d.h. für eine nichtleere Liste aus Kopf und Rest
• Es gilt: [Kopf] wird angehängt an die Liste RevRest, die mit Rest
in der Relation rev steht, also:
rev([Kopf|Rest],Res) :- ...
rev(Rest, RevRest), append(RevRest, [Kopf], Res)
• Was ist über Resultat ( Res) bekannt?
• damit Regel komplett:
append(...,[Kopf],Res)
rev([Kopf|Rest],Res) :- rev(Rest,RevRest), append(RevRest,[Ko
• An welche Liste wird die einelementige Liste [Kopf] angehängt?
• Programm komplett zusammen mit Fakt für leere Liste:
rev([],[]).
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 163
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 164
Prolog: Such- und Ausführungsordnung
Prolog: Abarbeitung von Zielen
s.a. [M.L.Scott, 2000, 11.3]
Für logische Ableitungen sind prinzipiell zwei Suchstrategien möglich:
• Der Suchraum bei der Strategie der Rückwärtsverkettung lässt sich darstellen als ein sog. AND-OR-Baum
• Gehe von existierenden Klauseln (den ’Axiomen’) aus und versuche, daraus durch wiederholte Resolutionsschritte das Ziel abzuleiten (sog. Vorwärtsverkettung oder forward chaining)
• Gehe von Ziel aus und versuche es rückwärts so ’aufzulösen’, dass existierende Klauseln erreicht werden (sog. Rückwärtsverkettung oder backward
chaining)
• Prolog verwendet backward chaining
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 165
• Ein AND-OR-Baum besteht aus sich abwechselnden AND- und OR-Ebenen
• Auf einer AND-Ebene liegen die konjunktiv verknüpften Teilziele der rechten Seite einer Regel
• Auf der darunterliegenden OR-Ebene liegen die alternativen Möglichkeiten
(disjunktiv verknüpft), ein Teilziel der darüberliegenden Ebene zu erfüllen,
d.h. alle die Hornklauseln aus der Datenbasis, deren linke Seite mit dem
darüberliegenden Teilziel unifiziert werden kann.
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 166
Prolog: Abarbeitung von Zielen
Prolog: Abarbeitung von Zielen
• Der AND-OR-Baum wird vom Prolog-Interpreter in Form einer Tiefensuche
(depth first search) durchlaufen.
• Aus der Abarbeitungsstrategie ergibt sich, dass linksrekursive Regeln zu Problemen bei
der Termination führen können
• Die OR-Alternativen werden dabei in dem Sinne nacheinander von links
nach rechts verarbeitet, dass zunächst mit einer Alternative und der daraus
resultierenden Instantiierung von Variablen so lange weitergearbeitet wird,
bis das Ursprungsziel erfüllt ist oder bis ein Teilziel fehlschlägt.
• Im letzteren Fall wird zur letzten nicht behandelten OR-Alternative zurückgegangen und – nachdem die seit diesem Punkt erfolgten Instantiierungen
aufgehoben wurden – dort mit der Suche fortgefahren (sog. backtracking)
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 167
• Beispiel: ein gerichteter, azyklischer Graph (vgl. [M.L.Scott, 2000, 11.3])
edge(a, b).
edge(d, e).
edge(b, c).
edge(b, e).
edge(c, d).
edge(d, f).
• mögliche Regel für Pfade in diesem Graph:
path(X,X).
path(X,Y) :- edge(Z,Y),path(X,Z).
• Was passiert bei folgender Anórdnung?
path(X,Y) :- path(X,Z), edge(Z,Y).
path(X,X).
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 168
Meta-Programmierung in Prolog
Meta-Programmierung in Prolog
Klauseln als Terme (vgl. Clocksin u. Mellish, Ch. 6.4)
Zusammenhang zwischen Prolog-Klauseln und Strukturen
• Klauseln können als gewöhnliche Prolog-Strukturen betrachtet und behandelt werden
• Fakt: Struktur ist Prädikat mit den Argumenten
likes(john,X).
• Möglichkeiten:
• Regel: Struktur mit Hauptfunktor ’:-’ (als Infix-Operator) und zwei Argumenten
– Struktur konstruieren, die Klausel in Datenbasis darstellt
– eine Klausel zur Datenbasis hinzufügen
– eine Klausel aus Datenbasis entfernen
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), – erstes Argument: Kopf
– zweites Argument: Körper
– m.a.W.: Regel likes(john,X) :- likes(X,wine)
entspricht Struktur ’:-’(likes(john,X),likes(X,wine))
169
Meta-Programmierung in Prolog
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 170
(Meta-)Prädikate zum Untersuchen und Ändern von
Klauseln
Zusammenhang zwischen Prolog-Klauseln und Strukturen cont.
• mehrere Ziele im Körper werden als durch den zweiargumentigen InfixOperator ’,’ verknüpft betrachtet
• clause(X,Y) . . . kann erfüllt werden, wenn sich X und Y mit Kopf bzw.
Körper einer in der Datenbasis existierenden Klausel unifizieren lassen
• Beispiel:
• X muss dabei soweit instantiiert sein, dass das Hauptprädikat der Klausel
feststeht
grandparent(X,Z) :- parent(X,Y),parent(Y,Z)
• kann auch für Fakten (mit dem Körper true) angewendet werden
entspricht
’:-’(grandparent(X,Z), ’,’(parent(X,Y),parent(Y,Z)))
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 171
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 172
(Meta-)Prädikat clause
(Meta-)Prädikate cont.
• Beispiel:
• asserta(X) . . . Hinzufügen der Klausel X an Anfang der DB
• assertz(X) . . . Hinzufügen der Klausel X an Ende der DB
• X muss dabei soweit instantiiert sein, dass das Hauptprädikat feststeht
?- clause(append(A,B,C),Y).
• der Effekt des Hinzufügens wird beim Backtracking nicht aufgehoben, sondern nur durch explicites retract(X)
• retract(X) . . . die erste Klausel, die mit X unifizierbar, wird aus der DB
entfernt
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 173
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), (Meta-)Prädikate für Strukturen: Konstruktion und Zugriff
(vgl. Clocksin u. Mellish, Ch. 6.5)
174
(Meta-)Prädikate . . . Strukturen cont.
• arg(N,T,A)
• functor(T,F,N) . . . T ist eine Struktur mit Funktor F und Arität N
• erstes und zweites Argument müssen instantiiert sein
• Verwendungen:
– T instantiiert (zumindest Hauptprädikat): Funktor wird extrahiert und Arität bestimmt
– T uninstantiiert, aber F und N instantiiert: Ergebnis ist konstruierte Struktur
• arg(N,T,A) erfolgreich, wenn N-tes Argument von Struktur T mit A unifizierbar
• Beispiele:
• Beispiel:
copy(Old,New) :- functor(Old,F,N),functor(New,F,N).
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 175
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 176
(Meta-)Prädikate . . . Strukturen cont.
Operatoren zur Verknüpfung von Zielen
• X =.. L (sprich: univ)
. . . L ist die Liste, die aus dem Funktor von X gefolgt von den Argumenten
von X besteht;
• zwei Verwendungsformen:
– X instantiiert, Liste wird konstruiert und mit L zu unifizieren versucht
– X uninstantiiert, aber L mit Atom als erstem Element instantiiert; Struktur
X wird konstruiert
(vgl. Clocksin u. Mellish, Ch. 6.7)
Konjunktion:
• ’,’ als vordefinierter rechtsassoziativer Infixoperator, d.h. X,Y,Z entspricht X,(Y,Z)
• X,Y . . . erfolgreich, wenn X und Y erfolgreich; wenn Y fehlschlägt, wird X
erneut zu erfüllen versucht (Backtracking); wenn X fehlschlägt, schlägt die
Konjunktion fehl
• Beispiele:
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 177
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), Verknüpfung von Zielen cont.
178
Verknüpfung von Zielen cont.
Disjunktion:
Disjunktion: cont.
• ’;’ als vordefinierter rechtsassoziativer Infixoperator, d.h.
person(X) :- (X=adam; X=eve; mother(X,Y)).
entspricht
person(X) :- ’;’(X=adam, ’;’(X=eve, mother(X,Y)))
• Semantik von ’;’:
– X;Y ist erfolgreich, wenn X erfolgreich oder Y erfolgreich;
– wenn X fehlschlägt, wird versucht, Y zu erfüllen;
– wenn Y fehlschlägt, schlägt die Disjunktion fehl
• alternative Darstellung:
person(adam).
person(eve).
person(X) :- mother(X,Y).
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 179
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 180
(Meta-)Prädikate für Ziele
(Meta-)Prädikate für Ziele cont.
(vgl. Clocksin u. Mellish, Ch. 6.7)
• call(X) . . . X muss als Term so instantiiert sein, dass dieser als Ziel interpretierbar
• call(X) ist erfolgreich, wenn X erfüllt werden kann, sonst Fehlschlag
• not(X) ist erfolgreich, wenn X fehlschlägt, not(X) schlägt fehl, wenn X
erfolgreich
• negation as failure
• wichtig für Ziele, die erst zur Laufzeit konstruiert werden
..., Z=..[P,X,Y], call(Z), ...
(dabei seien P als Funktor und X und Y als geeignete Argumente instantiiert)
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), • not(X) . . . X muss als Term so instantiiert sein, dass dieser als Ziel interpretierbar
181
Beachte: Unterschied zwischen
?- member(X,[a,b,c]), write(X).
und
?- not(not(member(X,[a,b,c]))), write(X).
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 182
Beziehung zwischen not und !
Beziehung zwischen not und !
• Cut zur Bestätigung der Regelauswahl durch Verwendungen von not ersetzbar
• Cut durch not zu ersetzen, macht i.A. Programme lesbarer, aber ggf. aufwendiger
– Beispiel:
A :- B,!,C.
A :- D.
– ersetzbar durch
A :- B,C.
A :- not(B),D.
– für sum_to:
sum_to(1,1).
sum_to(N,R) :- not(N=1), N1 is N-1,
sum_to(N1,R1), R is R1+N.
– bzw.
sum_to(N,1) :- N=<1.
sum_to(N,R) :- not(N=<1), N1 is N-1,
sum_to(N1,R1), R is R1+N.
• mögliche Darstellung von not:
not(P) :- call(P),!, fail.
not(P).
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 183
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 184
Anwendung von asserta und retract
Anwendung von asserta und retract
Beispiel: Erzeugung von Zufallszahlen (vgl. Clocksin u. Mellish, Ch. 7.8.1)
• Verwendung:
• random(R,N) . . . instantiiert N mit einer zufälligen ganzen Zahl zwischen
1 und R
• mögliche Definition:
:- dynamic seed/1.
seed(13).
random(R,N) :- retract(seed(S)), N is (S mod R) + 1,
NewSeed is (125*S+1) mod 4096,
asserta(seed(NewSeed)),!.
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), • durch repeat kann wiederholtes Erzeugen einer Zufallszahl (bis eine Bedingung erfüllt) erreicht werden; z.B.:
?- repeat, random(10,X), write(X), nl, X=7.
185
Anwendung von asserta und retract
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 186
Meta-Programmierung in Prolog:
Map: Prädikat auf alle Listenelemente anwenden (vgl. Clocksin u. Mellish,
Ch. 7.12)
• Variante:
...
seed(13).
random(R,N) :- seed(S), N is (S mod R) + 1,
retract(seed(S)),
NewSeed is (125*S+1) mod 4096,
asserta(seed(NewSeed)),!.
• maplist(P,L,M) . . . erfolgreich durch Anwenden des zweiargumentigen
Prädikats P auf jedes einzelne Element der Liste L und ‘Kreieren’ einer
neuen Liste M mit den bei Anwenden des Prädikats gewonnenen Ergebnissen
• mögliche Definition:
• aufgrund der Semantik von retract (Unifikationen bleiben erhalten) ist
die obige, einfachere Lösung gleichwertig
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), ?- random(10,X)
X = 4 ;
No
?- listing(seed).
:- dynamic seed/1.
seed(1626).
Yes
187
maplist(_,[],[]).
maplist(P,[X|L],[Y|M]) :Q =.. [P,X,Y], call(Q), maplist(P,L,M).
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 188
maplist
Meta-Programmierung in Prolog:
Checklist: Prüfen, ob Prädikat auf alle Listenelemente zutrifft
• Beispiele:
?- maplist(fak, [1,2,3,4,5], Z).
• checklist(P,L) . . . erfolgreich, wenn einargumentiges Prädikats P auf
jedes einzelne Element der Liste L zutrifft
?- maplist(plus(2), [1,2,3,4,5], Z).
• mögliche Definition:
checklist(_,[]).
checklist(P,[X|L]) :Q =.. [P,X], call(Q), checklist(P,L).
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 189
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), checklist:
190
Meta-Programmierung in Prolog:
Findall: Finden aller Lösungen (vgl. Clocksin u. Mellish, Ch. 7.8.3)
• Beispiele:
• findall(X,G,L) . . . ‘konstruiert’ eine Liste aus all den Objekten X, für
die das Ziel G erfüllt und unifiziert diese mit der Liste L
dabei: G ist als Term instantiiert, der als Ziel interpretierbar und der Variable
X irgendwo enthält
?- checklist(var, [X,Y,Z]).
?- checklist(var, [1,atom | X]).
• Beispiel:
?- findall(X,append(_,[X,X|_],[1,2,2,2,3,1,3,3]),B).
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 191
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 192
findall:
bagof und setof:
• entspricht bagof/3 mit allen freien Variablen mit dem Existenzquantor ^
gebunden mit dem einzigen Unterschied, dass bagof/3 fehlschlägt, wenn
das Ziel keine Lösung hat
• bagof(X,G,L) . . . bestimmt eine Liste von Werten für X, für die das Ziel
G erfüllt, und unifiziert diese mit der Liste L
• wenn Ziel G freie Variable enthält, wird für jede Belegung dieser Variable
die Bestimmung der Werte für L getrennt durchgeführt
• mit dem Existenzquantor ^ kann angegeben werden, welche Variablen als
gebunden betrachtet werden sollen
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 193
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), bagof:
setof:
Beispiele:
• setof(X,G,L) . . . analog zu bagof(X,G,L); nur ist L sortiert und enthält keine mehrfachen Vorkommen
?- listing(foo).
foo(a,
foo(a,
foo(b,
foo(b,
foo(c,
b,
b,
c,
c,
c,
194
• Beispiel:
c).
d).
e).
f).
g).
?- setof(X,append(_,[X,X|_],[1,2,2,2,3,1,3,3]),B).
?- bagof(C, foo(A, B, C), Cs).
?- bagof(C, A^foo(A, B, C), Cs).
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 195
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 196
Funktionale vs. logische Programmierung:
Funktionale vs. logische Programmierung:
Vergleich
theoretische Basis der Berechnungen
• funktionale Programme unterscheiden zwischen Eingaben (Argumente
von Funktionen) und Ausgaben (Werte von Funktionen)
• funktionale Programme: Substitution
• relationale Programme in Prolog: Unifikation und Resolution
• relationale Programme treffen diese Unterscheidung nicht notwendigerweise
• was Eingabe und was Ausgabe einer Berechnung mit einem relationalen
Programm sein soll, wird durch die Art der Invokation festgelegt (m.a.W.
höhere Flexibilität)
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 197
Funktionale vs. logische Programmierung:
198
Fallbeispiel: Umdrehen einer Liste cont.
Fallbeispiel: Umdrehen einer Liste
• Haskell: mit Pattern matching
• Prolog:
rev :: [a] -> [a]
rev([],[]).
rev([X|Y],Z) :- rev(Y,Z1), append(Z1,[X],Z).
rev [] = []
rev (x:xs) = rev xs ++ [x]
• Haskell: Variante mit Fallunterscheidung
• Scheme:
rev :: Eq a => [a] -> [a]
(define (rev l)
(if (null? l) l
(append (rev (cdr l)) (list (car l)))))
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), rev x = if x == [] then []
else rev (tail x) ++ [head x]
199
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 200
Funktionale vs. logische Programmierung:
Fallbeispiel: Mergesort cont.
Fallbeispiel: Mergesort (cf. [Mueller & Page, Ch. 34])
• merge is predefined, redefined as mymerge
• Prolog:
mymerge([], X, X).
mymerge(X, [], X).
mergesort([], []) :- !.
mergesort([X], [X]) :- !.
mymerge([X1|R1], [X2|R2], [X1|R]) :- precedes(X1, X2),
mymerge(R1, [X2|R2], R).
mergesort(X, Sorted) :- split(X, X1, X2),
mergesort(X1, S1),
mergesort(X2, S2),
merge(S1, S2, Sorted).
mymerge([X1|R1], [X2|R2], [X2|R]) :- precedes(X2, X1),
mymerge([X1|R1],R2, R).
• Prolog benötigt Hilfsvariable (hier: S1, S2), um Zwischenergebnisse zu
‘transportieren’
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 201
Fallbeispiel: Mergesort cont.
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 202
Fallbeispiel: Mergesort (cf. [Mueller & Page, Ch. 34])
• weitere Hilfsprädikate:
• Haskell:
/* split a list of n elements into two near halfes */
sort :: Ord a => [a] -> [a]
/* firstn(X, 0, [], X). */
firstn([X|Y], 1, [X], Y).
firstn([X|Y], N, [X|YN], RN) :- N1 is N-1,
firstn(Y, N1, YN, RN).
sort [] = []
sort [x] = [x]
sort seq = merge (sort a) (sort b)
where (a,b) = split seq
split(X, X1, X2) :- length(X,N), N2 is ceiling(N/2),
firstn(X, N2, X1, X2).
• flexible Kombination von Funktionsaufrufen
• in Verbindung mit Pattern zum Zugriff auf Elemente eines Paares
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 203
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 204
Fallbeispiel: Mergesort (cf. [Mueller & Page, Ch. 34])
Wichtiger Unterschied
• Hilfsfunktionen:
• Pattern matching in Prolog:
split :: [a] -> ([a],[a])
split [] = ([],[])
split seq = splitAt (div (length seq) 2) seq
all_equal([]).
merge :: Ord a => [a] -> [a] -> [a]
merge xs [] = xs
merge [] ys = ys
merge (x:xs) (y:ys)
| x == y
= x:(merge xs ys)
| x < y
= x:(merge xs (y:ys))
| otherwise
= y:(merge (x:xs) ys)
all_equal([X,X | Y]) :- all_equal([X | Y]).
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), all_equal([_]).
205
Wichtiger Unterschied cont.
206
Relationale Programme als ‘Generatoren’
• all_equal als Prädikat
• Pattern matching in Haskell:
?- all_equal([a,a,a,a]).
Yes
allEqual :: Eq a => [a] -> Bool
allEqual [] = True
allEqual [_] = True
allEqual (el1:el2:rest) = if el1 == el2
then allEqual (el2:rest)
else False
• all_equal als ‘Generator’
• Haskell: eine Variable darf in einem Pattern auf der linken Seite einer Definition nur einmal auftauchen
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 207
?- all_equal(X).
X = [] ;
X = [_G205] ;
X = [_G205, _G205] ;
X = [_G205, _G205, _G205] ;
...
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 208
Scheme als funktionale Sprache:
Scheme als funktionale Sprache:
• Diskussion von Scheme im Vergleich mit Haskell
• Funktionen Objekte erster Ordnung
• m.a.W. Gemeinsamkeiten und Unterschiede
1 ]=> (define pi 3.1415)
;Value: pi
• Haskell als ‘Blaupause’ für funktionale Sprachen
1 ]=> pi
;Value: 3.1415
Grundlegendes:
1 ]=> (define quadriere (lambda (zahl) (* zahl zahl)))
;Value: quadriere
• Interpreter
• Definition von Werten mit define
1 ]=> (quadriere pi)
;Value: 9.86902225
(define <name> <wert>)
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 209
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), Scheme: Anonyme Funktionen
210
Scheme: Anonyme Funktionen
alternative Syntax für benannte Funktionen
• Syntax:
• (define (<name> <par-1> ... <par-n>) <koerper>)
(lambda <parameterliste> <koerper>)
• überall dort verwendbar, wo auch mit Symbol auf Funktion verwiesen werden kann
1 ]=> ((lambda (n) (* n n)) 3)
• statt
(define <name>
(lambda (<par-1> ... <par-n>) <koerper>))
;Value: 9
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 211
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 212
Scheme vs. Haskell
Scheme: Übersicht
• in Scheme: Klammerung innerhalb geschachtelter Ausdrücke; in Haskell:
Vorrangregeln
• vordefinierte Datentypen
– Zahlen: ganze, rationale, Fliesskomma, ...
– Strings
– Zeichen
– boolesche Werte: #t, #f
• in Scheme: keine Typisierung; in Haskell: strenge Typisierung
• wichtigster aggregierter Datentyp: Liste
– Konstruktor: cons
– Selektoren: car, cdr
– Prädikate: null?
• Kontrollstrukturen:
– if
– cond
• Scheme: strikt; Haskell: non-strikt
• in Scheme nicht vordefiniert vorhanden sind u.a. Listenkomprehension
und Fallunterscheidung durch Pattern Matching
• in Haskell: verzögerte Auswertung (lazy evaluation) für alle Argumente
in Scheme: Auswertung für alle Argumente; aber: verzögerte Auswertung
und call-by-need möglich mit delay und force
• Funktionen zur Konversion zwischen verschiedenen Typen
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 213
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), Striktheit vs. Non-Striktheit
Scheme: Programm-Daten-Äquivalenz
• Eine (seiteneffekt-freie) Funktion heisst strikt, wenn gefordert wird, dass
alle ihre Argumente definiert sind und so die Evaluationsordnung das Ergebnis nicht verändert.
• Programme haben die Form von Listen
• Eine Funktion heisst non-strikt, wenn für sie die Forderung nach Striktheit
nicht erhoben wird.
• Programme können mit allen Listenfunktionen bearbeitet werden
• Eine Sprache heisst strikt, wenn gefordert wird, dass alle ihre Funktionen
strikt.
• Eine Sprache heisst non-strikt, wenn sie die Definition non-strikter Funktionen zulässt.
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 214
215
• Scheme (und Lisp) sind homoikonisch, d.h. selbstrepräsentierend
• Beispiel:
(define compose
(lambda (f g)
(lambda (x) (f (g x)))))
1 ]=> ((compose car cdr) ’(1 2 3))
;Value: 2
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 216
Scheme: weitere Aspekte
Scheme: Programm-Daten-Äquivalenz
• Funktionen mit beliebiger Anzahl von Argumenten möglich (sog. Restparameter, der an Liste gebunden)
• Beispiel cont.:
(define compose2
(lambda (f g)
(eval (list ’lambda ’(x) (list f (list g ’x)))
(scheme-report-environment 5))))
• Beispiel:
(define (avg . nums) (average nums))
1 ]=> ((compose2 car cdr) ’(1 2 3))
• average erwartet Liste als Argument
;Value: 2
• Definition:
(define (average nums)(/ (apply + nums) (length nums)))
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 217
Scheme: Funktionen höherer Ordnung
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 218
Scheme: Funktionen höherer Ordnung
• z.B. Falten einer zweistelligen Funktion in eine Liste:
• partielle Anwendungen sind bei Funktionen im Curry-Format möglich
(define fold
(lambda (f l i)
(if (null? l) i ;; identity for f
(f (car l) (fold f (cdr l) i)))))
(define curry
(lambda (f) (lambda (a) (lambda (b) (f a b)))))
1 ]=> (((curry +) 3) 4)
;Value: 7
(define curried-plus (curry +))
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 219
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 220
Prozeduren und Prozesse:
Prozeduren und Prozesse:
Beispiel: Berechnung Fakultät
Beispiel: Berechnung Fakultät als linear-rekursiver Prozess
Definition: n! = n * (n - 1) * . . . * 2 *1
(factorial 6)
(* 6 (factorial 5))
(* 6 (*5 (factorial 4)))
(* 6 (*5 (* 4 (factorial 3))))
(* 6 (*5 (* 4 (* 3 (factorial 2)))))
(* 6 (*5 (* 4 (* 3 (* 2 (factorial 1))))))
(* 6 (*5 (* 4 (* 3 (* 2 1)))))
(* 6 (*5 (* 4 (* 3 2))))
(* 6 (*5 (* 4 6)))
(* 6 (*5 24))
(* 6 120)
720
Zusammenhang: n! = n * (n - 1)!
(define (factorial n)
(if (= n 1)
1
(* n (factorial (- n 1)))))
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 221
Beispiel: Berechnung Fakultät als linear-iterativer Prozess
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 222
Beispiel: Berechnung Fakultät als linear-iterativer Prozess
in jedem Schritt:
(factorial 6)
(fact-iter
1 1 6)
(fact-iter
1 2 6)
(fact-iter
2 3 6)
(fact-iter
6 4 6)
(fact-iter 24 5 6)
(fact-iter 120 6 6)
(fact-iter 720 7 6)
720
product <-- counter * product
counter <-- counter + 1
(define (factorial n) (fac-iter 1 1 n))
(define (fact-iter product counter max-count)
(if (> counter max-count)
product
(fact-iter (* counter product)
(+ counter 1)
max-count)))
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 223
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 224
Scheme vs. Haskell:
Scheme vs. Haskell: Sichtbarkeit von Definitionen
Sichtbarkeit von Definitionen
Beispiel cont.:
• die Definitionen auf dem Toplevel von Scheme sind ‘global’ sichtbar
1 ]=> (define (isEven n)
(if (< n 0) () (if (= n 0) #t (isOdd (- n 1)))))
• wechselseitige Bezugnahme in rekursiven Definitionen ist möglich
;Value: iseven
• Beispiel:
1 ]=> (isEven 5)
1 ]=> (define (isOdd n) (if (<= n 0) () (isEven (- n 1))))
;Value: ()
;Value: isodd
...
1 ]=> (isOdd 5)
;Value: #t
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 225
Lokale Definitionen
226
Scheme: lokale Bindungen mit let und let*
Motivation:
• Syntax: (let ((var1 val1) (var2 val2) ... (varN valN)) <body>)
• Semantik:
bei der Auswertung von <body> sind die in var1, var2, ..., varN gebundenen
Werte val1, val2, ..., valN verfügbar
• Vermeiden wiederholter Berechnungen
• Beispiel: addPairwiseRest als Äquivalent zu addPairwise’
• klarer strukturierter Code
• Beispiel:
eine Funktion addPairwise’, die korrespondierende Elemente zweier
Zahlenlisten addiert und – falls eine Liste keine Elemente mehr hat – den
aktuellen Rest der anderen an die Liste der Paarsummen anhängt
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 227
(define (addPairwiseRest list1 list2)
(let ((front (addPairwise list1 list2))
(minLength (min (length list1)(length list2))))
(let ((rear (append (drop minLength list1)
(drop minLength list2))))
(append front rear))))
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 228
Scheme: lokale Bindungen mit let und let*
Scheme: lokale Bindungen mit let und let*
• Beachte: bei let erfolgt die Auswertung und Bindung in den einzelnen
(var1 val1) (var2 val2) ... (varN valN) parallel
• let* ist wie let, nur erfolgt Auswertung und Bindung in den einzelnen
(var1 val1) (var2 val2) ... (varN valN) sequentiell
• daher folgendes falsch:
• m.a.W. für die Ausdrücke vali (für 2 <= i <= n) stehen die Bindungen
var1, var2, ..., var(i-1) zur Verfügung
(define (addPairwiseRest list1 list2)
(let ((front (addPairwise list1 list2))
(minLength (min (length list1)(length list2)))
(rear (append (drop minLength list1)
(drop minLength list2))))
(append front rear)))
1 ]=> (addPairwiseRest ’(4 7 1 1 ) ’(8 15))
;Unbound variable: minlength
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 229
Scheme: lokale Bindungen mit let und let*
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 230
Beispiel cont.
• Hilfsfunktionen a la Haskell:
• Beispiel für let*:
(define (addPairwiseRest list1 list2)
(let* ((front (addPairwise list1 list2))
(minLength (min (length list1)(length list2)))
(rear (append (drop minLength list1)
(drop minLength list2))))
(append front rear)))
(define (addPairwise list1 list2)
(if (or (null? list1)(null? list2))
()
(cons (+ (car list1)(car list2))
(addPairwise (cdr list1)(cdr list2)))))
(define (take n list)
(if (= n 0) ()
(cons (car list) (take (- n 1) (cdr list)))))
1 ]=> (addPairwiseRest ’(4 7 1 1 ) ’(8 15))
(define (drop n list)
(if (= n 0) list
(drop (- n 1) (cdr list))))
;Value 12: (12 22 1 1)
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 231
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 232
Blockstruktur:
Scheme: Berechnung Quadratwurzel mit Newton-Verfahren
(define (sqrt x)
(define (good-enough? guess x)
(< (abs (- (square guess) x)) .001))
(define (improve guess x)
(average guess (/ x guess)))
(define (sqrt-iter guess x)
(if (good-enough? guess x)
guess
(sqrt-iter (improve guess x) x)))
(define (sqrt x)
(sqrt-iter 1 x))
(define (sqrt-iter guess x)
(if (good-enough? guess x)
guess
(sqrt-iter (improve guess x) x)))
(define (good-enough? guess x)
(< (abs (- (square guess) x)) .001))
(sqrt-iter 1 x)
)
(define (improve guess x)
(average guess (/ x guess)))
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 233
Interne Definitionen
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 234
lexikalischer Skopus:
Ausnutzen, daß x in sqrt gebunden (lexikalischer Skopus):
• freie Variable in einer Prozedur verweisen auf Variable in umfassenden
Prozeduren;
(define (sqrt x)
(define (good-enough? guess)
(< (abs (- (square guess) x)) .001))
(define (improve guess)
(average guess (/ x guess)))
(define (sqrt-iter guess)
(if (good-enough? guess)
guess
(sqrt-iter (improve guess))))
• m.a.W.: Werte freier Variable werden in der Umgebung gesucht, in der die
Prozedur definiert wurde
(sqrt-iter 1))
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 235
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 236
Ströme: Motivation
Ströme: Sprachmittel in SCHEME
• Berechnungen auf Kollektionen von Daten
• Konstruktor: cons-stream
• abstrakt gesehen: Sequenz von Datenobjekten
• Selektoren: head, tail
• mögl. Implementation: durch Listen
• Beziehung: für bel. a,b und x:
Wenn x ist (cons-stream a b), dann (head x) ist a und (tail x)
ist b.
• bessere Implementation: mit verzögerter Auswertung
Ströme repräsentieren den zeitlichen Verlauf der durch sie modellierten Systeme
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 237
Ströme: Implementation
• Konstante: the-empty-stream
• Prädikat: empty-stream?
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 238
Implementation cont.
• Implementation von Strömen durch verzögerte Evaluation (delayed evaluation, lazy evaluation)
• in normalen Listen: car und cdr werden zur Konstruktionszeit evaluiert
• in Strömen: tail erst zur Zugriffszeit evaluieren (call-by-need)
• (cons-stream <a><b>) . . . Spezialform, die gleichwertig mit
(cons <a> (delay <b>))
– d.h. Strom wird als Paar dargestellt
– im cdr steht promise zur Berechnung des tail
• (define (head stream) (car stream))
• Spezialform delay:
(delay <exp>) . . . liefert ein verzögertes Objekt, sog. promise, dass erst
mit force zur Ausführung veranlasst wird
• (define (tail stream) (force (cdr stream)))
• (force <promise>) . . . veranlasst Ausführung von <promise>
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 239
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 240
Unendlich lange Ströme
Unendlich lange Ströme cont.
Sieb des Eratosthenes
• Strom positiver ganzer Zahlen
(define (sieve stream)
(cons-stream
(head stream)
(sieve
(filter
(lambda (x) (not (divisible? x (head stream))))
(tail stream)))))
(define (integers-starting-from n)
(cons-stream n (integers-starting-from (1+ n))))
(define integers (integers-starting-from 1))
• Strom von Fibonacci-Zahlen
(define (fibgen a b)
(cons-stream a (fibgen b (+ a b))))
(define primes (sieve (integers-starting-from 2)))
(define fibs (fibgen 0 1))
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 241
Implizit definierte Ströme
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 242
Implizit definierte Ströme cont.
Hilfsfunktion: Elementweises Addieren zweier Ströme
• Strom positiver ganzer Zahlen
(define (add-streams s1 s2)
(cond ((empty-stream? s1) s2)
((empty-stream? s2) s1)
(else
(cons-stream
(+ (head s1)(head s2))
(add-streams (tail s1) (tail s2))))))
(define ones (cons-stream 1 ones))
(define integers
(cons-stream 1 (add-streams ones integers)))
• Strom der Fibonacci-Zahlen
(define fibs
(cons-stream 0
(cons-stream 1
(add-streams (tail fibs) fibs))))
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 243
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 244
Funktionale Programmierung: Haskell
Funktionale Programmierung: Scheme
• rein funktional
• funktional und imperative Elemente
• non-strikt
• strikt
• strenge Typisierung
• keine Typisierung
• lazy evaluation
• eager evaluation (aber: delay)
• Listenkomprehension
• –
• –
• Programm-Daten-Äquivalenz
• Pattern matching in Definitionen
• –
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 245
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), Rein funktionale Sprachen
246
Funktionskomposition als Sprachmittel
• in Scheme:
• Programmieren ohne Seiteneffekte
(define (compose f1 f2) (lambda (x) (f1 (f2 x))))
• Seiteneffekte können Programme schwer zu lesen und schwer zu kompilieren machen
• Abwesenheit von Seiteneffekten:
– Ausdrücke sind referentiell transparent – unabhängig von Ausführungsordnung
– Denken in Gleichungen möglich
– Gleichheit zweier Ausdrücke ist nicht zeitabhängig
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 247
• Beispiel für Anwendung:
(define
(define
(define
(define
(flatten tree) ...)
(remove-duplicates list) ...)
list-of-atoms (compose remove-duplicates flatten))
nr-of-atoms (compose length list-of-atoms))
; 1 ]=> tree
;Value 6: ((a (b (c d) e) (b f g) h (c d)))
; 1 ]=> (list-of-atoms tree)
;Value 7: (a e b f g h c d)
; 1 ]=> (nr-of-atoms tree)
;Value: 8
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 248
Algebraische Typen
Produkttypen als algebraische Typen
Aufzählungstypen (vgl. [S.Thompson, 1999, 14.1])
• Beispiel:
• Beispiel:
data People = Person Name Age
type Name = String
type Age = Int
data Temp
= Cold | Hot
data Season = Spring | Summer | Autumn | Winter
• Pattern matching zur Definition von Funktionen
weather :: Season -> Temp
weather Summer = Hot
weather _
= Cold
• Beispiele:
Person "Hans Mueller" 42
Person "Bart Simpson" 12
• Definition von Gleichheit für Temp
Cold ==
Hot ==
_
==
Cold
Hot
_
= True
= True
= False
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 249
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), Produkttypen als algebraische Typen cont.
250
Rekursive algebraische Typen
• Pattern matching zur Definition von Funktionen
• Beispiel: arithmetische Ausdrücke mit ganzen Zahlen
showPerson :: People -> String
showPerson (Person st n) = st ++ " -- " ++ show n
data Expr = Lit Int|
Add Expr Expr|
Sub Expr Expr
• Konstruktor Person ist Funktion vom Typ
• Beispiele:
Person :: Name -> Age -> People
7
4+7
(7-4)+11
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 251
Lit 7
Add (Lit 4) (Lit 7)
Add (Sub (Lit 7)(Lit 4)) (Lit 11)
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 252
Rekursive algebraische Typen cont.
Rekursive algebraische Typen cont.
• Beispiel: Bäume mit ganzen Zahlen
• Funktionen mit Hilfe primitiver Rekursion
z.B. Auswerten
data NTree
=
eval :: Expr -> Int
eval (Lit n)
eval (Add e1 e2)
eval (Sub e1 e2)
NilT |
Node Int NTree NTree
• Beispiele für Funktionen:
= n
= (eval e1) + (eval e2)
= (eval e1) - (eval e2)
sumTree, depth :: NTree -> Int
sumTree NilT
= 0
sumTree (Node n t1 t2) = n + sumTree t1 + sumTree t2
...
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 253
Beispiele für Funktionen: cont.
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 254
Notizen
...
depth NilT
= 0
depth (Node n t1 t2) = 1 + max (depth t1)(depth t2)
occurs :: NTree -> Int -> Int
occurs NilT p
= 0
occurs (Node n t1 t2) p
| n==p
= 1 + occurs t1 p + occurs t2 p
| otherwise =
occurs t1 p + occurs t2 p
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 255
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 256
Polymorphe algebraische Typen:
Binäre Bäume als polymorphe algebraische Typen
Definitionen algebraischer Typen können Typvariable a, b, ... enthalten
(vgl. [S.Thompson, 1999, 14.3])
• die Elemente in den Knoten können von bel. Typ sein
• Beispiel:
• Beispiel:
data Pairs a
= Pr a a
data Tree a = Nil | Node a (Tree a) (Tree a)
deriving (Eq, Ord, Show, Read)
• einige Ausprägungen:
Pr 47 11
:: Pairs Int
Pr [] [42] :: Pairs [Int]
Pr [] []
:: Pairs [a]
• Beispiel:
• der eingebaute Listentyp könnte wie folgt definiert sein:
depth :: Tree a -> Int
depth Nil
= 0
depth (Node n t1 t2) = 1 + max (depth t1)(depth t2)
data List a = NilList | Cons a (List a)
deriving (Eq, Ord, Show, Read)
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 257
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), Binäre Bäume . . .
Binäre Bäume . . .
• aus Baum Liste ‘kollabieren’ bei Inorder-Traversierung
collapse
collapse
collapse
=
258
• mapTree als Funktion höherer Ordnung auf binären Bäumen
:: Tree a -> [a]
Nil
= []
(Node n t1 t2)
collapse t1 ++ [n] ++ collapse t2
mapTree :: (a -> b) -> Tree a -> Tree b
mapTree f Nil
= Nil
mapTree f (Node n t1 t2)
= Node (f n) (mapTree f t1) (mapTree f t2)
• Beispiel:
collapse (Node 7 (Node 4 Nil Nil)
(Node 1 (Node 1 Nil Nil) Nil))
= [4,7,1,1]
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 259
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 260
Algebraische Typen:
Algebraische Typen: Variante
für Produkttypen gibt es eine alternative Syntax:
• Instanzen werden kreiert mithilfe des Konstruktors und der benannten Selektoren
>p1 = Person’ {name = "Paul", age = 21}
• Beispiel (vgl. Folie 134):
• da der Selektor die jeweilige Komponente eindeutig benennt, ist die Reihenfolge der Angaben beliebig
data Person’ = Person’ {name :: Name, age :: Age}
deriving (Eq,Show,Read)
>p2 = Person’ {age = 12, name = "Pauline"}
• die Ausgabe durch die abgeleitete show-Funktion erfolgt aber in der Reihenfolge aus der
data-Definition
type Name = String
type Age = Int
• m.a.W.: statt einer ’positionalen’ Kodierung der Komponenten eines Produkttyps haben wir hier eine Kodierung mit benannten Selektoren
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 261
Main> p1
Person’{name="Paul",age=21}
Main> p2
Person’{name="Pauline",age=12}
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 262
Algebraische Typen: Variante
Algebraische Typen: Variante
• mit den benannten Selektorfunktionen kann auf die Werte der Komponenten zugegriffen
werden
• beachte: bei der Modifikation wird eine modifizierte Kopie der Ausgangsinstanz kreiert,
d.h. diese bleibt unverändert
Main> name p1
"Paul"
Main> age p2
12
Main> p1
Person’{name="Paul",age=21}
• Konstruktor und Selektoren können in Funktionen zur Modifikation von Instanzen verwendet werden
• Beispiel:
Main> hasBirthday p1
Person’{name="Paul",age=22}
Main> p1
Person’{name="Paul",age=21}
hasBirthday :: Person’ -> Person’
hasBirthday p = Person’ {name= name p, age= age p + 1}
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 263
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 264
Funktionale Programmierung: Haskell
Funktionale Programmierung: Scheme
• rein funktional
• funktional und imperative Elemente
• non-strikt
• strikt
• strenge Typisierung
• keine Typisierung
• lazy evaluation
• eager evaluation (aber: delay)
• Listenkomprehension
• –
• –
• Programm-Daten-Äquivalenz
• Pattern matching in Definitionen
• –
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 265
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), Rein funktionale Sprachen
266
Funktionskomposition als Sprachmittel
• in Scheme:
• Programmieren ohne Seiteneffekte
(define (compose f1 f2) (lambda (x) (f1 (f2 x))))
• Seiteneffekte können Programme schwer zu lesen und schwer zu kompilieren machen
• Abwesenheit von Seiteneffekten:
– Ausdrücke sind referentiell transparent – unabhängig von Ausführungsordnung
– Denken in Gleichungen möglich
– Gleichheit zweier Ausdrücke ist nicht zeitabhängig
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 267
• Beispiel für Anwendung:
(define
(define
(define
(define
(flatten tree) ...)
(remove-duplicates list) ...)
list-of-atoms (compose remove-duplicates flatten))
nr-of-atoms (compose length list-of-atoms))
; 1 ]=> tree
;Value 6: ((a (b (c d) e) (b f g) h (c d)))
; 1 ]=> (list-of-atoms tree)
;Value 7: (a e b f g h c d)
; 1 ]=> (nr-of-atoms tree)
;Value: 8
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 268
Herunterladen