4 auf 1

Werbung
3. Funktionales Programmieren
3.1 Grundkonzepte funktionaler Programmierung
3. Funktionales Programmieren
Beispiele: Funktionsabstraktion (3)
Funktionsdeklaration
3. Abstraktion über Funktionsbezeichner:
Ausdruck:
Abstraktion:
In Haskell gibt es unterschiedliche syntaktische Formen für die
Funktionsdeklaration:
f (f x)
\ f x -> f (f x)
1. mittels direkter Wertvereinbarung:
Mit Bezeichnervereinbarung:
<Funktionsbez >
twice = \ f x -> f (f x)
erg
= ( twice sqrt) 3.0
Beispiel:
twice2 = \ f -> \ x -> f (f x)
erg
= ( twice sqrt) 3.0
TU Kaiserslautern
3. Funktionales Programmieren
=
<Ausdruck von Funktionstyp >
fib = \ n -> if
n == 0 then 0
else if n == 1 then 1
else fib (n -1) + fib (n -2)
Äquivalente Vereinbarung:
©Arnd Poetzsch-Heffter
3.1 Grundkonzepte funktionaler Programmierung
217
©Arnd Poetzsch-Heffter
3.1 Grundkonzepte funktionaler Programmierung
TU Kaiserslautern
3. Funktionales Programmieren
Funktionsdeklaration (2)
218
3.1 Grundkonzepte funktionaler Programmierung
Funktionsdeklaration (3)
3. mittels formalen Parametern und Fallunterscheidung über
Wächtern:
<Funktionsbez > <Parameterbez1 > ...
| <boolscher Ausdruck > = <Ausdruck >
...
| <boolscher Ausdruck > = <Ausdruck >
2. mittels einem oder mehreren formalen Parametern:
<Funktionsbez > <Parameterbez1 > ...
=
<Ausdruck >
Beispiel:
Die boolschen Ausdrücke in der Deklaration heißen Wächter,
engl. guards.
fib n = if
n == 0 then 0
else if n == 1 then 1
else fib (n -1) + fib (n -2)
Beispiel:
fib
|
|
|
n
n == 0
= 0
n == 1
= 1
otherwise = fib (n -1) + fib (n -2)
Das Schlüsselwort otherwise steht hier für True.
©Arnd Poetzsch-Heffter
TU Kaiserslautern
219
©Arnd Poetzsch-Heffter
TU Kaiserslautern
220
3. Funktionales Programmieren
3.1 Grundkonzepte funktionaler Programmierung
3. Funktionales Programmieren
Funktionsdeklaration (4)
3.1 Grundkonzepte funktionaler Programmierung
Funktionsdeklaration (5)
4. mittels Fallunterscheidung über Mustern:
<Funktionsbez > <Parametermuster > ... =
...
<Funktionsbez > <Parametermuster > ... =
<Ausdruck >
5. mittels Kombinationen der Formen 3 und 4.
<Ausdruck >
Beispiel:
Muster sind ein mächtiges Programmierkonstrukt, das weiter
unten genauer behandelt wird.
fib
fib
|
|
Beispiel:
0 = 0
n
n==1
= 1
otherwise = fib (n -1) + fib (n -2)
fib 0 = 0
fib 1 = 1
fib n = fib (n -1) + fib (n -2)
©Arnd Poetzsch-Heffter
TU Kaiserslautern
3. Funktionales Programmieren
221
3.1 Grundkonzepte funktionaler Programmierung
TU Kaiserslautern
3. Funktionales Programmieren
Bemerkungen:
222
3.1 Grundkonzepte funktionaler Programmierung
Beispiel: rekursive Funktionsdekl.
1. Einzelne rekursive Funktionsdeklaration:
• Jeder Funktionsdeklaration sollte die Funktionssignatur
vorangestellt werden und ein Kommentar, der mindestens die
Voraussetzungen an die Parameter beschreibt.
rpow :: Float -> Integer -> Float
-- rpow r m verlangt : m >= 0
rpow r n = if n == 0 then 1.0
else r * rpow r (n -1)
Beispiel:
fib :: Integer -> Integer
-- fib k verlangt : k >= 0
fib 0 = 0
fib 1 = 1
fib n = fib (n -1) + fib (n -2)
2. Verschränkt rekursive Funktionsdeklaration:
gerade
:: Integer -> Bool
ungerade :: Integer -> Bool
-- Bedingung an Parameter n bei beiden Funktionen :
-- n >= 0
gerade
n = (n == 0) || ungerade (n -1)
ungerade n = if n == 0 then False else gerade (n -1)
• Die Form einer Funktionsdeklaration sollte so gewählt werden,
dass die Deklaration gut lesbar ist.
©Arnd Poetzsch-Heffter
©Arnd Poetzsch-Heffter
TU Kaiserslautern
223
©Arnd Poetzsch-Heffter
TU Kaiserslautern
224
3. Funktionales Programmieren
3.1 Grundkonzepte funktionaler Programmierung
3. Funktionales Programmieren
Deklaration rekursiver Funktionen
3.1 Grundkonzepte funktionaler Programmierung
Definition: (rekursive Funktionsdekl.)
Begriffsklärung: (rekursive Definition)
Eine Funktionsdeklaration heißt direkt rekursiv, wenn der
definierende Ausdruck eine Anwendung der definierten Funktion
enthält.
Eine Definition oder Deklaration nennt man rekursiv, wenn der
definierte Begriff bzw. das deklarierte Programmelement im
definierenden Teil verwendet wird.
Eine Menge von Funktionsdeklarationen heißt verschränkt rekursiv
oder indirekt rekursiv (engl. mutually recursive), wenn die
Deklarationen gegenseitig voneinander abhängen.
Bemerkung:
• Rekursive Definitionen finden sich in vielen Bereichen der
Informatik und Mathematik, aber auch in anderen Wissenschaften
und der nichtwissenschaftlichen Sprachwelt.
• Wir werden hauptsächlich rekursive Funktions- und
Eine Funktionsdeklaration heißt rekursiv, wenn sie direkt rekursiv ist
oder Element einer Menge verschränkt rekursiver
Funktionsdeklarationen ist.
Datentypdeklarationen betrachten.
©Arnd Poetzsch-Heffter
TU Kaiserslautern
3. Funktionales Programmieren
225
©Arnd Poetzsch-Heffter
3.1 Grundkonzepte funktionaler Programmierung
TU Kaiserslautern
3. Funktionales Programmieren
3.1 Grundkonzepte funktionaler Programmierung
Begriffsklärung: (rekursive Funktion)
Zur Auswertung von Funktionsanwendungen:
Eine Funktion heißt rekursiv, wenn es rekursive
Funktionsdeklarationen gibt, mit denen sie definiert werden kann.
Sei f x = A[x] ;
Eine Funktionsanwendungen f e kann nach unterschiedlichen
Strategien durch Verwendung der Deklarationsgleichungen
ausgewertet werden, zum Beispiel call-by-value:
Bemerkungen:
• Die Menge der rekursiven Funktionen ist berechnungsvollständig.
• Rekursive Funktionsdeklarationen können als eine Gleichung mit
einer Variablen verstanden werden, wobei die Variable von einem
Funktionstyp ist:
Beispiel:
Gesucht ist die Funktion f , die folgende Gleichung für alle n ∈ Nat
erfüllt:
f n = if n = 0 then 1 else n ∗ f (n − 1)
©Arnd Poetzsch-Heffter
TU Kaiserslautern
227
226
• Werte Ausdruck e aus; Ergebnis nennt man den aktuellen
Parameter z .
• Ersetze x in A[x] durch z .
• Werte den resultierenden Ausdruck A[z] aus.
Haskell benutzt die Auswertungsstrategie call-by-need (siehe 3.4).
Beispiele: (Rekursion)
siehe Vorlesung
©Arnd Poetzsch-Heffter
TU Kaiserslautern
228
3. Funktionales Programmieren
3.1 Grundkonzepte funktionaler Programmierung
3. Funktionales Programmieren
Begriffsklärung: (lineare/repetitive Rekursion)
Beispiele:
Vereinfachend betrachten wir hier nur Funktionsdeklarationen, bei
denen die Fallunterscheidung „außen“ und die rekursiven Aufrufe in
den Zweigen der Fallunterscheidung stehen.
• Die übliche Definition von fac ist nicht repetitiv, da im Zweig der
rekursiven Anwendung die Multiplikation an äußerster Stelle steht.
• Die folgende Funktion facrep ist repetitiv:
• Eine rekursive Funktionsdeklaration heißt linear rekursiv, wenn
facrep :: Integer -> Integer -> Integer
-- facrep n res verlangt : n >= 0 && res >= 1
facrep n res = if n == 0 then res
else facrep (n -1) (res*n)
in jedem Zweig der Fallunterscheidung höchstens eine rekursive
Anwendung erfolgt (Beispiel: Definition von fac).
• Eine rekursive Funktionsdeklaration heißt repetitiv (rekursiv),
wenn sie linear rekursiv ist und die rekursiven Anwendungen in
den Zweigen der Fallunterscheidung an äußerster Stelle stehen.
©Arnd Poetzsch-Heffter
TU Kaiserslautern
3. Funktionales Programmieren
fac n = facrep n 1
229
©Arnd Poetzsch-Heffter
3.1 Grundkonzepte funktionaler Programmierung
230
3.1 Grundkonzepte funktionaler Programmierung
Beispiel: (kaskadenartige Rekursion)
• Eine rekursive Funktionsdeklaration für f heißt geschachtelt
rekursiv, wenn sie Teilausdrücke der Form f (. . . f (. . . ) . . . ) enthält.
• Eine rekursive Funktionsdeklaration für f heißt kaskadenartig
Berechne:
Wie viele Kaninchen-Pärchen leben nach n Jahren, wenn man
• am Anfang mit einem neu geborenden Pärchen beginnt,
• jedes neu geborene Pärchen nach zwei Jahren und dann jedes
rekursiv, wenn sie Teilausdrücke der Form
h(. . . f (. . . ) . . . f (. . . ) . . . ) enthält.
TU Kaiserslautern
TU Kaiserslautern
3. Funktionales Programmieren
Begriffsklärung: (Geschachtelte Rekursion)
©Arnd Poetzsch-Heffter
3.1 Grundkonzepte funktionaler Programmierung
folgende Jahr ein weiteres Pärchen Nachwuchs erzeugt und
• die Kaninchen nie sterben.
231
©Arnd Poetzsch-Heffter
TU Kaiserslautern
232
3. Funktionales Programmieren
3.1 Grundkonzepte funktionaler Programmierung
3. Funktionales Programmieren
Beispiel: (kaskadenartige Rekursion) (2)
3.1 Grundkonzepte funktionaler Programmierung
Bemerkung:
Die Anzahl der Pärchen stellen wir als Funktion ibo von n dar:
• vpr dem 1. Jahr:
ibo(0) = 1
• nach dem 1. Jahr:
ibo(1) = 1
• nach dem 2. Jahr:
ibo(2) = 2
• Aus Beschreibungssicht spielt die Form der Rekursion keine Rolle;
wichtig ist eine möglichst am Problem orientierte Beschreibung.
• Aus Programmierungssicht spielt Auswertungseffizienz eine
• nach dem n. Jahr:
wichtige Rolle, und diese hängt von der Form der Rekursion ab.
Beispiel:
Kaskadenartige Rekursion führt im Allg. zu einer exponentiellen
Anzahl von Funktionsanwendungen (z.B. bei ibo 30 bereits
1.664.079 Anwendungen).
die Anzahl aus dem Jahr vorher plus die Anzahl der im n. Jahr
Geborenen; und die ist gleich der Anzahl vor zwei Jahren, also:
ibo n = ibo(n − 1) + iob(n − 2) für n > 1.
Insgesamt ergibt sich folgende kaskadenartige Funktionsdeklaration:
ibo
n =
if n<=1 then 1 else ibo (n -1) + ibo (n -2)
©Arnd Poetzsch-Heffter
TU Kaiserslautern
3. Funktionales Programmieren
233
©Arnd Poetzsch-Heffter
3.1 Grundkonzepte funktionaler Programmierung
TU Kaiserslautern
3. Funktionales Programmieren
Unterabschnitt 3.1.3
234
3.1 Grundkonzepte funktionaler Programmierung
Die Datenstruktur der Listen
Eine Liste über einem Typ T ist eine total geordnete Multimenge mit
Elementen aus T (bzw. eine Folge, d.h. eine Abb. Nat -> T ).
Eine Liste heißt endlich, wenn sie nur endlich viele Elemente enthält.
Listen und Tupel
Haskell stellt standardmäßig eine Datenstruktur für Listen bereit, die
bzgl. des Elementtyps parametrisiert ist. Typparameter werden
üblicherweise geschrieben als a, b, ...
©Arnd Poetzsch-Heffter
TU Kaiserslautern
235
©Arnd Poetzsch-Heffter
TU Kaiserslautern
236
3. Funktionales Programmieren
3.1 Grundkonzepte funktionaler Programmierung
3. Funktionales Programmieren
Die Datenstruktur der Listen (2)
Typ:
Die Datenstruktur der Listen (3)
[a] , a ist Typparameter
Dem Typ [a] ist als Wertemenge die Menge aller Listen über
Elementen vom Typ a zugeordnet.
Funktionen:
(==), (/=)
(:)
(++)
head, last
tail, init
null
length
(!!)
take, drop
::
::
::
::
::
::
::
::
::
[a] → [a] → Bool
a → [a] → [a]
[a] → [a] → [a]
[a] → a
[a] → [a]
[a] → Bool
[a] → Int
[a] → Int → a
Int → [a] → [a]
3.1 Grundkonzepte funktionaler Programmierung
wenn (==) auf a definiert
Notation:
In Haskell gibt es eine vereinfachende Notation für Listen:
statt
x1 : x2 : ... : xn : []
kann man schreiben:
[ x1 , x2 , ..., xn ]
Konstanten:
[]
©Arnd Poetzsch-Heffter
:: [a]
TU Kaiserslautern
3. Funktionales Programmieren
237
©Arnd Poetzsch-Heffter
3.1 Grundkonzepte funktionaler Programmierung
TU Kaiserslautern
3. Funktionales Programmieren
Beispiele: (Funktionen auf Listen)
238
3.1 Grundkonzepte funktionaler Programmierung
Beispiele: (Funktionen auf Listen) (2)
1. Addiere alle Zahlen einer Liste von Typ [Int]:
3. Zusammenhängen zweier Listen (engl. append):
mapplus :: [Int] -> Int
mapplus xl = if null xl then 0
else (head xl) + mapplus (tail xl)
append :: [a] -> [a] -> [a]
append l1 l2 = if l1 == [] then l2
else (head l1):( append (tail l1) l2)
mapplus [1 ,2 ,3 ,4 ,5 ,6]
4. Umkehren einer Liste:
2. Prüfen einer Liste von Zahlen auf Sortiertheit:
ist_sortiert :: [Int] -> Bool
ist_sortiert xl = if null xl || null (tail xl)
then True
else if (head xl)<=(head (tail xl))
then ist_sortiert (tail xl)
else False
©Arnd Poetzsch-Heffter
TU Kaiserslautern
239
rev :: [a] -> [a]
rev xl = if null xl then []
else append (rev (tail xl)) [head xl]
©Arnd Poetzsch-Heffter
TU Kaiserslautern
240
Herunterladen