to get the file

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), 249
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), 250
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), Produkttypen als algebraische Typen cont.
251
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), 252
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), 253
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), 254
Beispiele für Funktionen: cont.
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 255
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), 256
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 257
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), 258
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
=
259
• 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), 260
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 261
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), 262
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), 263
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), 264
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), 265
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), 266
c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007
Sommer 2007, Programmierparadigmen (PGP), Rein funktionale Sprachen
267
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), 268
• 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), 269
Herunterladen