Pro Informatik: Funktionale Programmierung

Werbung
Autor: Martin Lenders
Pro Informatik: Funktionale Programmierung
Einführung
Was ist überhaupt Programmierung?
Was ist ein Programm?
Wozu programmieren wir?
Was ist der Computer für ein Gerät?
Wieso muss man ihn programmieren? Einen Toaster muss man schließlich auch nicht
programmieren...
28.07.2008
Ein Computer ist eine deutlich kompliziertere Technologie, während ein Toaster nur zu einem
„Programm in der Lage ist“. Die Progrmame eines Computers hingegen helfen einem die
unterschiedlichsten Probleme zu lösen.
VERSCHIEDENE ARTEN VON PROGRAMMIERSPRACHEN:
Objektorientiert
imperativ
funktional
strukturell
Funktionale Programmierung basiert auf der Idee Ausdrücke zu berechnen
HASKELL UND HUGS
Zwei Endungen der Programm-Daten (Wahl ist Kommentarabhängig)
.hs, -- Kommentar; Programm: addDrei x y z = x + y + z
.lhs, Kommentar; Programm: > addDrei x y z = x + y + z
INTEGER
Int: x, y, z sind ganzzahlig
Int: 32 Bit
Operatoren: + - * `div`
Signatur: addDrei::Int->Int->Int->Int
BOOLEAN
Bool: logischer Wert: True oder False
Bool: 1 Bit
Verknüpfungs-Operatoren (Vergleichbarer Wert Bool): > < >= <= == /=
boolsche Operatoren (Bool Bool): && (logisches UND), || (logische ODER), not (logisches Nicht)
Guard-Operator:
maxi
:: Int -> Int -> Int
maxi x y | x >= y
= x
| otherwise = y
Fallunterscheidung
Lazy evaluation: sollte ein Ergebnis schon vor Beendigung einer Prüfung klar sein, bricht Haskell ab
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
Haskell: Fortsetzung Einführung
29.08.2008
BINDUNGSSTÄRKE VON OPERATOREN
linksassoziativ
9
Nicht assoziativ
!, !!, //
.
**, ^, ^^
8
7
*, /, `div`, `mod`,
`rem`, `quot`
6
+, -
:+
5
\\
4
/=, <, <=, ==
3
>, >=, `elem`
:, ++
&&
||
2
1
rechtsassoziativ
>>, >>=
0
$, `seq`
CHARACTER
Char: Zeichen
Char: 32 Bit (ASCII-codiert 'A' – 'Z' =65 – 90; 'a' – 'z' =97 – 122
FLOATING-POINT
Float: Gebrochene Zahlen
Float: 32 Bit
Operatoren: + - * / ...
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
Beweis mit vollständiger Induktion über n, mit n 01.08.2008
s. Kopie
Schema: Was zu beweisen ist über
n , n (Behauptung)
Beweis mit vollständiger Induktion über n, mit n . Induktionsanfang: Bewesi der Behauptung für n = 1
. Induktionsschritt:
1. Induktionsvorraussetzung: sei n , dann gilt die Behauptung...
2. Induktionsbehauptung: ...dann gilt die Behauptung auch für n + 1
3. Beweis von 2.
Beweise:
n
k
k1
nn1
, n
2
Beweis durch vollständige Induktion über n
. Induktionsanfang: Sei n = 1
n
1
n n1
1 11
1
k k 1
2
2
k1
k 1
. Induktionsschritt:
1. Induktionsvorraussetzung: Sei n , dann gilt
n
nn1
k 2
k1
2. Induktionsbehauptung:
n1
n1
n2 Dann gilt auch: k 2
k1
3. Beweis der Induktionsbehauptung
n 1
n
n n1
n1
k k n1
2
k 1
k1
(IV)
2
nn1
2n1
n 3n2 n1
n2
2
2
2
2
double
:: [Int] -> [Int]
(1) double []
= []
(2) double (x:xs) = 2*x:double xs
sum
(3) sum []
(4) sum (x:xs)
:: [Int] -> Int
= []
= x+sum xs
Beweise: sum(double ys) == 2*(sum ys)
wobei n == legnth ys, n Essentiell, sonst Behaubtung sinnlos
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
Beweis durch vollständige Induktion über n = Länge der Liste
. Induktionsanfang: Sei n = 0, wobei n == length ys
sum double ys
sum double sum 0
(1)
(3)
2 sum ys
2 sum 2 0 0
(3)
. Induktionsschritt:
1. Induktionsvorraussetzung: Sei n , dann gilt für n == length yx
(IV)
sum double ys
2 sum ys
2. Induktionsbehauptung: Dann gilt auch:
sum double y : ys
2 sum y : ys
3. Beweis:
sum double y : ys
sum 2 y : double ys
2 y sum double ys
(2)
(4)
2 sum y : ys
2 y 2 sum ys
2 y sum ys (IV)
(4)
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
MafI-Exkurs + Wiederholung 1. Woche
04.08.2008
1. MENGEN
Elemente werden zusammengefasst in einer Menge
1.1 Eine endliche Menge kann durch ihre Elemente angegeben werden
Sei X eine Menge mit: x : x1, .., x n
heißt „definiert“
x i ist Element X x iX ,i1,.. ,n
x 1,.. , x n gilt, das sie Elemente von x sind für alle x i ,i1, .. , n : x i X
x i ,i1, .. , n : x i X
: heißt „es gilt für 'links', was 'rechts' steht“
Die leere Menge :enthält keine Elemente
Die Elementen von erfüllen alle Eigenschaften
Für alle
1.2 Teilmengen
Eine Menge X' heißt Teilmenge von X, wenn gilt
X 'X : x X ' : x X
Zwei Mengen A und B können vereinigt werden zu einer Gesamtmenge
AB
x AB : x Ax B
Zwei Menge A und B können miteinander geschnitten werden
AB
x AB : x Ax B
AB
Aus einer Menge X kann man Teilmengen generieren, die durch gewisse Eigenschaften der Elemente
charakterisiert sind.
X ' :x X : x hat die Eigenschaft E
ZF.Notation in Haskell: [ x | x X, x hat die Eigenschaft E]
Bsp. xs = { - 1, 2, 4, -13, 6 }
[x | x <-xs, x > 0]
ZF-Notation: kurz für Zermelo-Fraenkel-Notation (Erfinder)
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
1.3 Beziehung zwischen Mengen
Seien X und Y zwei Mengen unter einer Abbildung (Abb' von X und Y versteht man eine Vorschrift f,
die jeder x X eindeutig ein f x Y zuordnet)
x X ist das Abbild
f x Y ist das Bild von x unter f
f: X Y
x f (x)
Vergleiche Haskell:
func::Int->Int
func x = x+1
1.4 Eigenschaften von Funktionen
Gegeben sei X, Y Menge und f mit f: X Y, x f(x)
f ist injektiv gdw (genau dann wenn), x , y X : x y f x f y Bsp:
f: Z Z
x x²
f(2) = 4
f(-2) = 4
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
f ist surjektiv
es existiert in X mindestens ein
f ist bijektiv
x X :
f x y
injektiv und surjektiv
Seien X ,Y , Z Mengen mit f : X Y sowie
dann heißt die Abb.: g f
: X Z , x g
Die Komposition von f und g: In Haskell:
func x = x+1
> func (func 1)
> (func . func) 1
(.) :: (b x) (a b) a c
(.) g f x = g (f(x))
g: Y Z , Abbildung
f x : g f x
, x X
Seien A und B Mengen, das Kreuzprodukt von A und B ist:
wie zip in Haskell
AB a , b
: a A , bB Seien
G , H Mengen und eine Abbildung mit : GG H
a , b
a , b
a b
(Kurzschreibweise)
ist kommutativ, wenn gilt a bb a ; a , b G
ist assoziativ, wenn gilt a b
ca
b
c
;
a , b , cG
Unterschied: Listen Mengen: Elemente einer Liste haben ein feste Position
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
Sortieralgorithmen und Laufzeitanalyse
05.08.2008
iSort [12,0,6,1]
(iSort.2)
ins 12 (isort [0,6,1])
(iSort.2)
ins 12 (ins 0 (isort [6,1]))
(iSort.2)
ins 12 (ins 0 (ins 6 (isort [1])))
(iSort.2)
ins 12 (ins 0 (ins 6 (ins 1 (isort []))))
(iSort.1)
ins 12 (ins 0 (ins 6 (ins 1 [])))
(ins.1)
ins 12 (ins 0 (ins 6 [1]))
(ins.3)
(6 <= 1) = ins 12 (ins 0 (ins 1 (ins 6 [])))
(ins.1)
ins 12 (ins 0 (ins 1 [6]))
(ins.2)
(1 <= 6) = ins 12 (ins 0 (1:[6]))
ins 12 (ins 0 [1,6])
(ins.2)
(0 <= 1) = ins 12 (0:[1,6])
ins 12 ([0,1,6])
(ins.3)
(12 <= 0) = 0:(ins 12 [1,6])
(ins.3)
(12 <= 1) = 0:(1:(ins 12 [6]))
(ins.3)
(12 <= 6) = 0:(1:(6:(ins 12 [])))
(ins.3)
(12 <= 6) = 0:(1:(6:[12]))
0:(1:[6,12])
0:[1,6,12]
[0,1,6,12]
LAUFZEITANALYSE VON ALGORITHMEN
n , n T A n :ist die Anzahl der einzelnen Auswertungsschritte für Eingabegröße n
Sei A ein Algorithmus mit Eingabegröße
Unterscheidung von drei Fällen der Eingabe
worst-case:
Eingabe erfordert maximal viele Schritte
best-case:
Eingabe erfordert minimal viele Schritte
average-case:
Eingabe erfordert durchschnittlich viele Schritte
Insertion-Sort:
1. Fall (best-case): Liste ist bereits vorsortiert
T iSort 0
T iSort n
=1
= Einfügen von einem Element in die Liste“ +
T iSort n 1
= 2 + 2 T iSort n 2
=2+
T iSort n 1
// Ein Schrit Vergleich, ein Schritt (:)
=2+2+2+
T iSort n 3
n
= 2 + 2 + .. + 2 + 1 =
2 1
= 2n + 1 ≤ 3n
i1
Die O-Notation
Seine N, M Mengen, g eine Funktion
g: N M , dann ist.
O g
: f : !c"0, !x 0, x# x 0 :$ f x
$%$cg x $
T iSort O g
mit g n
nc3, x 01
Schreibweisen:
T iSort O g
mit ...
T iSort O g mit ...
T iSort O n
iSort n
O n
O n
formal korrekt jedoch nur die erste
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
2. Fall (worst-case): Liste ist verkehrt herum sortiert
T iSort 0
=1
T iSort n
T iSort n
= füge ein Element ein +
=
≤
=
=
=
=
T iSort n
T iSort n
n² n
≤
≤
≤
T iSort n 1
n 1
T iSort n 1
nT iSort n 1
nn 1
T iSort n 1
nn 1
n 2
..1
n n1
1
n²n
2
2
g n
n² , c n2
4g n
n² , c2
4g n
2n²
4n²
O n²
3. Fall (zufällig): Verteilung der Elemente zufällg
T iSort 0
= 1
T iSort n
=
T iSort n
=
n
T iSort n 1
2
n n
n² n
1
2
2
n n 1
2
n² n
T iSort n 1
2
2
2
2
4
O (n²)
qSort
1. Fall: Liste bereits sortiert (auf./absteigend)
T iSort 0
= 1
T iSort n
T 0 2T n 1
2 n 1
= 32n 2T n 1
= 12nT n 1
= n 2nO n2 =
2. Fall: Die Liste ist unsortiert und verteilt sich gleichmäßig rechts und links des Pivotelements
T iSort 0
= 1
T iSort n
T n &2
2T n &2
2 n 1
2 T n &2
n
= 2 T n &2
n
O n log 2 n
=
=
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
Wdh. O-Notation
06.08.2008
Motivation: Wir möchten Algorithmen miteinander vergleichen/bewerten
Generelle Frage: Welchen Aufwand („Komplexität“) hat ein Algorithmus?
Aufwand? Komplexität? Wie operationalisiere ich das?
Man betrachtet, wie viele „Ressourcen“ der Algorithmus benötigt
(Speicher (Platz), Laufzeit (Zeit))
I. Laufzeitanalyse von Algorithmen
1.
Möglichkeit:
2.
Möglichkeit:
Warum?
Betrachten der Rechenzeit auf einem konkreten Rechner, mit konkreter Eingabe und
Algorithmus programmiert in einer konkreten Programmiersprachen.
Vorteil: Unterschiede zwischen Hardwarekomponenten erkannt
Nachteil: nicht algorithmenspezifisch.
Wir abstrahieren von einem konkreten Rechner (eigentlich auch von Darstellung in
einer bestimmten Programmiersprache) und zählen die sogenannten „Elementar“Schritte (Funktionsaufrufe, numerische, logische Auswertungen = alle „kosten“ 1).
Uns interessiert wie viele Elementarschritte im Verhältnis von Eingave und ihrer
Größe gemacht werden. Es interessiert nicht der Zeitaufwand auf einem bestimmten
Rechner mit einer bestimmten Eingabe, sondern wie der Zeitbedarf (Anzahl der
Schritte) wächst, wenn die Eingabegröße wächst.
Dabei betrachtet man die Fälle:
schlechtesten Fall (worst-case) : Aufwand eines Algorithmus im schlechtesten Fall bei der
Eingabe einer bestimmten Größe
durchschnittlichen Fall (average-case): Aufwand eines Algorithmus gemittelt über alle Eingaben
einer bestimmten Größe
bester Fall (best-case): Aufwand eines Algorithmus im besten Fall bei der Eingabe einer
bestimmten Größe
Was genau wird jetzt gemacht?
Man betrachtet die Anzahl aller Elementarschritte eines Algorithmus A im Verhältnis zur Eingabe und der
Größe und nennt das die Laufzeit von A. Im Ergebnis erhält man die Laufzeitanalyse.
Dabei wird die Laufzeit mit TA(n) angegeben wobei n die Eingabegröße darstellt
Beispiel :
sumL []
= 0
sumL (x:xs) = x + sumL xs -- Sei n die Länge der Eingabeliste,
n
T sumL 0
1
T sumL n
111T sumL n 1
3T sumL n 1
33...313 n1
d. h. für die Funktion sumL benötigt ein beliebiger Rechner 3 n + 1 Elementar-Schritte
subS::String->String->[Int]
subS pat xs = subSi pat xs 1
subSi::String->String->Int->[Int]
subSi pat [] p = []
subSi [] xs p = []
subSi pat (x:xs) p
|(take (length pat) (x:xs) == pat) = [p] ++ subSi pat xs (p+1)
| otherwise = subSi pat xs (p+1)
Laufzeitanalyse von subS, Sei n die Länge der ersten Eingabeliste (length pat == n), m die Länge der
zweitern Eingabeliste (length (x:xs) == m); n , m
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
T subS 0, m
11114 Aufruf, Erstes Pattern bei SubSi und zweites und Zuweisung
T subS n ,0 1113
T subS n , m
31
n1
T
take n , m
1 T
subS n , m 1
9nmT subS n , m 1
s. ZR (1)
%m3 s. ZR (2)
da subSi in diesem Fall subS
entsprechend, da p const.
9nm9nm 1T subS n , m 2
182n2m 1T subS n , m 2
m
2
2
3m m
m m
9 mmnm i 39nmm 39nm
3
2
2
2
2
i1
2
ZR:
(1)
length pat
length [] = 0
length (x:xs) = 1 + length xs
T length 0
1
T length n
1T length n 1
11...1
1n1
n mal
(2)
take k (x:xs) – wobei k == length pat
take 0 xs = []
take n [] = []
take n (x:xs) = x:(take (n-1) xs)
Sei a die erste Eingabegröße (a == k), sei b die zweite Eingabegröße (b == length (x:xs))
T take 0, b
1
T take a ,0
2
T take a , b
31t take a , b
1. Fall: a'b(T a , b
1T take a 1, b 1
1..1
T 0, b a
a1
a mal
2. Fall: a#b : (T a , b
1T take a 1, b 1
1..1
T a b , 0
b1
b mal
Fall 1 & 2: T take a , b
1T take a 1, b 1
%1b11b3
b da length pat immer ≤ length xs
Rekursionsbaum
Man kann einen sogenannten Baum zeichnen, um Rekursionstiefe darzustellen. Das ist ein Modell, das
Divide&&Conquere Vorgehensweise im Algorithmus darstellt
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
Funktionen: Weiterführende Themen
07.08.2008
Funktionen als Werte
add::Int->(Int->Int) add::Int->Int->Int
(add x ) y = x + y
während
Anonyme Funktionen in Lambda-Notation
y z x + y + z (Lambda-Notation) in Haskell: \y z -> x + y + z
z. B.
add3::Int->Int->Int->Int
add3 x = (\y z-> x+y+z) add3 x y z = x + y + z
!"#
$$$%#
$$$
Hugs „weiß“, dass er eine Funktion ausgeben muss, weiß aber nicht wie er es darstellen soll
Partielle Funktionsanwendung
map::(a->b)->[a]->[b]
iter::Int->Int
iter x = x+1
&
#'(()
'((+)
&*'(()
'((+)
(+)::Int->(Int->Int)
(((+) x) y) = ...
>(+1)
'-> \y (y + 1)
> map (2+) [1,2,3]
'(+(,)
&$'(()
'(+(-)
&*.$'(()
'(,(/)
0
#1%%233%4'(+(5()
'+(5)
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
LÖSUNG ÜBUNG 2.0
a) 0 `mod` 2
Richtig, da mod-Funktion in Infix-Schreibweise genutzt wird
berechnet den Modulus von 0/2 0
b) ((`mod` 2) == 0)
Falsch, da Funktion (`mod` 2)::Int->Int (\x-> x `mod` 2) mit Int verglichen wird
c) flop f b a = f a b
Richtig, wenn Signatur flop::(x->y->z)->y->x->z
z.B.
lustig::Bool->Int->Char
lustig t z | (z < 0) && t = 'a'
| otherwise = 'B'
>flop lustig 3 True
'B'
d) (* 8 12)
falsch, da Operator in Infix-Schreibweise, jedoch Präfix genutzt
e) f a b c = g a b c + g a (b-1) c
where g x y = (* `div` x y)
falsch, da div-Funktion in Infix-Schreibweise, und um div x y Klammern fehlen
die scheinbar falsche Übergabe von 3 Parametern an g ist richtig, da g x y eine Funktion zurückgibt,
die c als Parameter erhält (lazy Programing!!!)
Die Signaturen von f und g sind dann entsprechend f::x->x->x->x und g->x->x->(x->x)
z. B.
>f 1 2 3
'-> g 1 2 3 + g 1 1 3
'-> ((*) (div 1 2)) + ((*) (div 1 1))
'-> ((*) 0) 3 + ((*) 1) 3
'-> (\3 -> 3 * 0) + (\3 -> 3 * 1)
'-> 0 + 3
'-> 3
f) 7 * 3 – ( -2)
Falsch, da bei 3 - (-2) der Funktion (3-)::Int->Int eine Funktion (Int->Int) in Form von
(-2) (\x = x – 2) mit der Signatur (-2)::Int->Int übergeben wird
g) (+) 3 (2*5)
Richtig, da (+)::Int->Int->Int und (*)::Int->Int->Int, damit ist das Ergebnis 13
h) (+) 3 ((2*) 5 )
Richtig, da Funtion (2*)::Int->Int an (+)->Int->(Int->Int) übergeben wird
Das Ergebnis ist 13
i) (+) 3 2 * 5
Richtig, da +-Operator in Präfix-Schreibweise. Formell wird die (+) Funktion als erstes ausgeführt, da
Funktionen in Haskell die stärkste Bindung besitzen und zudem linksassoziativ sind
Das Ergebnis ist 25
j) (3+) (2*) 5
Falsch, da die Funktion (3+)::Int->Int die Funktion (2*)::Int->Int als Parameter erhält (durch die
Linksassozivität von Funktionen). Korrekt wäre (3+) ((2*) 5)
k) (3+2 *) 5
Falsch, da der Compiler durch die stärkere Bindung von *, diesen zuerst auswertet, was zur Folge
hat, das (3+2 *) einen Fehler erzeugt, da * ein `)` als Parameter erhält
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
func::(a->b)->(b->b->c)->(a->a->c)
func f g = (\x y -> g (f x) (f y))
>func (+2) (*) 1 3
'-> (\1 3 -> (*) ((+2) 1) ((+2) 3)
'-> ((*) 3 5)
'-> 15
>func (*2) (+) 1 3
'-> (+) ((*2) 1) ((*2) 3)
'-> (+) 2 6
'-> 8
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
Currying
08.08.2008
Def:
„Currying“ nennt man den Vorgange eine n-stellige Funktion in eine 1-stellige zu verwanden, die ein
(n-1)-stellige Funktion zurück gibt.
Eine uncurried Funktion muss n Parameter zu einem zusammenfassen.
f::a->(b->c)
-- curried function
g::(a,b)->c
-- uncurried function
Curried functions sind nützlich, weil sie partielle Funktionsanwendung erlauben
Bsp:
add3::Int->(Int->(Int->Int))
add3 x (\y z -> x + y + z)
foldl::(a->b->a)->a->[b]->a
> foldl (&&)
'-> (\s l -> foldl (&&) s l)
curry::((a, b)->c)->(a->b->c)
curry g x y = g (x, y)
[Bild 1, S. 185]
uncurry::(a->b->c)->((a, b)->c)
uncurry g (x, y) = g x y
[Bild 2, S. 185]
Bsp:
multiply :: Int -> Int -> Int
multiply x y = x * y
multiplyUC :: (Int, Int) -> Int
multiplyUC (x, y) = x * y
gegeben x , y curry multiplyUC x y
gegeben x , y , uncurry multiply (x,y)
Einschub: Klassifizierung von Rekursionen
Bsp.
fak 0 = 1
fak n = n * fak(n-1)
fib 0 = 0
fib 1 = 1
fib n = fib(n-2) + fib (n-1)
Definition: Eine Funktion f heißt linear rekursiv, wenn es in jedem Definitionszweig von f höchstens einen
Rekursionsaufruf gibt, sonst nicht linear (im Beispiel: fak linear; fib nicht linear).
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
foldl f e [] = e
foldl f e (x:xs) = foldl f (f e x) xs
Definition: Ein rekursiver Aufruf heißt schlicht, wenn er einen direkten Wert für die aufgerufene Funktion
liefert
Definition: Eine Funktion heißt endrekursiv, wenn alle rekursiven Aufrufe schlich sind
foldl ist endrekursiv
smod x y
| x >= 2*y = smod(smud x 2*y) y
| x >= y
= smod(x-y) y
| x < y
= x
Definition: Ein rekursiver Aufruf heißt verschachtelt, wenn der rekursive Aufruf als Argument einen
rekursiven Aufruf enhält (smod ist verschachtelt)
Definition: Rekursive Funktionen, ohne verschachtelte Aufrufe heißen primitiv rekursiv, sonst nicht primitiv
rekursiv
Entrekusivierung
sum :: [Int]->Int
sum [] = 0
sum (x:xs) = x + sum xs
> sum [3, 4, 5]
'-> 3 + sum[4, 5]
'-> 3 + (4 + sum [5])
in jedem rekursiven Aufruf merken
'-> 3 + (4 + (5 + sum []))
addiert werden muss.
'-> 3 + (4 + (5 + 0))
da Addition zum Schluss erledigt wird, ist die Funktion nicht entrekursiv.
was
Lösung: Endrekursive Formulierung mit Akkumulator
sumA::[Int]->Int->Int
sumA [] akk = akk
sumA (x:xs) akk = sumA xs (akk+x)
> sum [3,4,5]
'-> sumA [3,4,5] 0
'-> sumA [4,5] (0+3)
'-> sumA [5] (3+4)
'-> sumA [] (7+5)
'-> 12
fakA::Int->Int->Int
fakA 0 akk = akk
fakA n akk = fak (n-1) (akk*n)
reverse' l = reverseA l []
where reverseA :: [a] -> [a] -> [a]
reverseA [] akk = akk
reverseA (x:xs) akk = reverseA xs (akk ++ [x])
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
Aufgabe': Create an index
Eingabe: "cathedral doggerel catherdral\nbattery doggerel
cathedral\ncathedral\ncathedral"
&6
% 478
8
# "
#0
((
77
#
0
Eingabe lines numLines allNumWords sortLs makeLists amalgamate shorten
type Doc = String
type Line = String
type Word = String
lines::Doc->[Line] --Eingabestring wird zerlegt in einzelne
--Zeilen
numLines::[Line]->[(Int,Line)] –-Nummerierung der Zeilen
allNumWords::[(Int,Line)]->(Int,Word)]
--zerlegt jede Zeile in Wörter und nummeriert
--sie entsprechend der Zeile
sortLs::[(Int,Word)]->[(Int,Word)] --Sortiert Liste lexikografisch
makeLists::[(Int,Word)]->[([Int],Word)]
--wandelt Int in [Int] um
amalgamtente::[([Int],Word)]->[([Int],Word)]
--fasst alle gleichen Wörter zusammen
shorten::[([Int],Word)]->[([Int],Word)]
--entfernt doppeltes Vorkommen von Ints in List
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
Probeklausur-Besprechung & Induktion Funktionen höherer Ordnung
12.08.2008
length (reverse xs ++ [x]) == 1 + length xs benötigt (!!) Extrabeweis.
Beweise: length (reverse xs) length xs
mit length xs = n, n 0
I Induktionsanfang: s. meine Lösung
II.Induktionsschritt:
1. Induktionsvorraussetzung:
für ein bel. n 0, n == length xs:
(IV)
length (rewerse xs) == length xs
2. Induktionsbehauptung:
(n+1), n == length xs:
length (reverse (x:xs)) == length (x:xs)
3. Induktionsbeweis
lengthreverse x : xs length reverse xs x rev.2
length reverse xs
length x length xslength x NB
IV length x length xs length x xs
NB
length x :xs
length x : xs
.2
.1
(NB)
Beweise: length a + length b = length (a++b)
mit length a == n, n 0 und length b == m, mit m 0 bel. aber fest
I Induktionsanfang:
für n = 0:
length alength b length length b
0length blength b
len.1
length ab
length b length b
.1
II.Induktionsschritt:
1. Induktionsvorraussetzung:
für ein bel. n == length a, n 0 und length b == m, mit m 0 bel. aber fest:
(IV)
length a + length b == length (a ++ b)
2. Induktionsbehauptung:
(n+1), n == length a und b = m, mit m 0 bel. aber fest:
length (a:as) + length b == length ((a:as) ++ b)
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
3. Induktionsbeweis
length a : as length b 1length aslength b
len.2
1length asb length a : asb
IV
len.2
length a : as
b
.2
Rechenbeispiel 1: s. Thompson, S. 196
im Deutschen nur der „Kram“ zu beachten der davor steht:
Beweis durch vollständige Induktion über die Länge der Liste n
Sei n 0, Länge der Eingabeliste xs...
Beweise:
filter p. map f xsmap f . filter p . f xs
Beweis durch vollständige Induktion über die Länge der Liste n
Sei n 0, Länge der Eingabeliste xs
I. Induktionsanfang:
für (n == 0)
filter p . map f xs filter p . map f filter p map f filter p comp.1
map1.1
fil.1
map f . filter p . f xsmap f. filter p . f map f map f filter p . f comp.1
fil.1
q.e.d.
map.1
II. Induktionsschritt:
1. Induktionsvorraussetzung:
für ein bel. n 0, n == length xs
filter p. map f xsmap f . filter p . f xs
2. Induktionsbehauptung:
(n+1) , n == length xs
filter p . map f x : xs map f . filter p . f x : xs Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
3. Beweis:
filter p . map f x : xs filter p map f x : xs comp.1
filter p f x : map f xs
map.2
1. Fall: p (f x) ist True
f x : filter p . map f xs
f x : filter p map f xs fil.2
comp.1
f x : map f . filter p . f xs
IV
2. Fall: p (f x) ist False
filter p . map f xs
filter p map f xs
fil.2
comp.1
map f . filter p . f xs
IV
map f . filter p . f x : xs map f filter p . f x : xs comp.1
1. Fall p(f x) ist True
f x : map f filter p. f xs map f x : filter p . f xs fil.2
map.2
f x : map f . filter p . f xs
q. e. d. 2. Fall p(f x) ist False
comp.1
map f filter p . f xs
fil.2
map f . filter p . f xs
q. e. d.
comp.1
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
Der -Kalkül
13.08.2008
MOTIVATION
der Lambda-Kalkül wurde entwickelt um berechenbare Funktionen zu definieren
) ist in erster Linie ein theoretisches Modell für die Berechenbarkeit (s. auch Turingmaschine)
) entwickelt maßgeblich von A. Curch (in den 30er Jahren
Gleichzeitig ist der Lambda-Kalkül eine Grundlage für die Funktionalen Programmiersprachen
) Mathematischer Formalismus zur Beschreibung von Funktionen mit Hilfe von
Rechenvorschriften, die angewendet auf ein Argument ein Ergebnis liefern
1. DER LAMBDA-KALKÜL DEFINIERT SOGENANNTE -AUSDRÜCKE
)
)
die grundlegende Operation ist eine Anwendung f(x) einer Funktion auf ein Argument x
Bsp.: Sei
mult:(* × *) N
mult(m,n) m · n
mit Hilfe von f kann eine neue Funktion definiert werden, indem das neue Argument gebunden
und f übergeben wird. (Abstraktion)
Argument
Bsp.
x .
x .y n
func
x = ...
mult x , x
+3
bindet x x steht für das Argument
beschreibt ein Argument, bei dem n ein aktueller Marameter ist
Funktionskopf Rumpf
> func 4
die Parameter übergabe ist „call-by-name“, also textueller Ersetzung
des formalen durch den aktuellen Parameter.
Jeder -Ausdruck ist eine Variable, Abstarktion oder Anwendung
<
-Ausdruck> <Variable> | <Abstraktion> | <Applikation>
<Variablen> x | y | ... | f
<Abstraktion> <Variable> . <
-Ausdruck>
<Applikation> (<
-Ausdruck>) <
-Ausdruck>
: definiert
|: oder
<bla>: Platzhalter
z. B. x . (
y . z . (x) y) z
Alternative Definition:
<
-Ausdruck> <Variable> | <Abstraktion> | <Applikation>
<Variablen> x | y | ... | f
<Abstraktion> (
<Variable> . <
-Ausdruck>)
<Applikation> (<
-Ausdruck> <
-Ausdruck>)
z. B. (
x . ((
y . (
z . (x y))) z))
(vgl. obiges Bsp.)
BN-Form: Backus-Nauer-Form
Bsp.: x ist <
-Ausdruck> , <Variable>
x . x ist <
-Ausdruck> , <Abstarktion>
(y) z ist <
-Ausdruck> , <Applikation>
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
SYNTAX-BAUM (PARSE-TREE)
Gegeben sei der -Ausdruck A- x .- y . x
b
a :
1. Beginne mit Startsymbol <
-Ausdruck>
2. Ersetze Metasymbole durch ihre definierten Ausdrücke
3. Wiederhole Ersetzung so lange bis keine Metasymbole mehr vorkommen und A dargestellt ist
4. ist A korrekt, wenn er vollständig wiedergegeben wurde.
2. DEFINITION: FREIE / GEBUNDENE VARIABLEN
1. FV(x) {x}
2. FV(
x.E) FV(E) {x}
3. FV((P)Q) FV(P) FV(Q)
Sei A ein -Ausdruck, ist FV(A) die Menge aller freien Variablen von A
{bla}: Menge
zu :
Seien M und N Mengen
M N Die Menge M ohne die Elemente aus N
z. B.: {1,2,3} {3} = {1,2}
z. B.:
FV y . x FV x . y x . y x (2)
(1)
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
FV - y . - x . x
y
FV - x . x y . y
2
FV x y . x . y 2 FV x FV y . x . y 3
x y . x . y 1
x , y . x . y y. y 3. DEFINITION
wird Redex genannt
-Reduktion
- x . E Q
+
(
/
E x &Q
wird ausgewertet zu
E [x/Q] Ausdrücke der entsteht, wenn alle freien Variablen E durch Q ersetzt werden
Beispiel:
- x . x y ( y
/
- x . x - z . z ( - z . z
/
- y .- x . x
y
- y . y
x
a
(
- x . x a
- y . y x
/
( - y . y
x
a
1. Schritt: FV ((
x . (x) y) y . (y) x)
/
( a
x
/
Sei f eine Funktion mit f: × , (n, m) n · m
x . f x , x
2 ( f 2, 2
/
Sei g eine Funktion mit (a, b) a + b
y . x . g f x , x , y
1 2
( y .
x . g f x , x , y
1 2
E
Q
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
-Konversion & Typklassen in Haskell
14.08.2008
-KONVENTION
Bsp.
- x . y . x y y b
E
Q
1. Redex
Umbenennung von y nach z
(
- x .- z . x
z y
b
0
( - z . y
z b
/
( y b
/
Die -Konversion erlaubt gebundene Variablen umzubennen, um Konflikte zu vermeiden:
Sei x =<Variable> und E,Q = <
-Ausdruck>, die -Konversion ist definiert als:
(
x.E)Q
( (
y.E‘)Q, wobei alle freien Vorkommen von x in E durch y ersetzt werden
- x . x y . y
z
E
Q
Umbenennung von y nach z
( - y . y
z
/
z
(
/
TYPKLASSEN
entsprechen, die Implementierung betreffend, in etwa Interfaces in Java
dazu entsprechend Instances: Klassen mit Interface
func::Eq a, b => (a, a) -> (Bool, a)
func (a, b) = (a == b, b)
ERMITTELN VON SIGNATUREN
1. Welche / Wieviele Paramerter hat die Funktional?
2. Was passiert mit denParametern => auf Datentyp schließen
3. Finde einen gemeinsamen Datentyp zu jedem Parameter, sodass die Operationen ausgeführt
werden können.
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
Algebraische Datentypen
type
type
type
type
type
15.08.2008
Vorname = String
Nachname = String
Strasse = String
PLZ = String
Ort = String
data Adresse = A Vorname Nachname Strasse PLZ Ort
-- -------------------------------type Sorte = String
type Farbe = String
type Gewicht = Int
data Apfel = A Sorte Farbe Gewicht
data Birne = B Sorte Farbe Gewicht
data Obst Aa Apfel | Bb Birne
instance Eq Obst where
(==) (Aa (A s1 f1 g1)) (Bb (B s2 f2 g2)) = False
(==) (Aa (A s1 f1 g1)) (Aa (A s2 f2 g2)) = (s1 == s2) &&
(f1 == f2) &&
(g1 == g2)
(==) (Bb (B s1 f1 g1)) (Bb (B s2 f2 g2)) = (s1 == s2) &&
(f1 == f2) &&
(g1 == g2)
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
Binäre Bäume
18.08.2008
DEFINITION VON GRAPHEN
Sei K eine Menge von Knoten. Ein Graph G ist (K, E) mit E x , y ; x ,y K ,xy
= Menge der Knoten
Bsp.
Sei K die Menge aller U-Bahnstationen in Berlin
K = {k1, .. , kn}
Eki ,k j ; ki ,k j K , gdw. zwischen ki und kj eine
k1 = Dahlem Dorf
Verbindung existiert = {(k2, k3), (k3, k4)}
k2 = ...
Sei G Graph mit K (Knotenmenge) und E (Kantenmenge)
K = {k1, .. , kn}, E = {e1, .. , en}, graphische Darstellung mit e1 = (k1, k2)
DEFINITION VON EINEM BAUM
Definition: Ein Baum ist ein Graph G = (K, E), der
1. zusammenhängend ist
2. kreisfrei ist
Graph ist nicht
kreisfrei
zusammenhängend: von jedem Knoten kann man jeden anderen über einen Knotenweg erreichen
kreisfrei: Für keinen Knoten x gibt es einen Knotenzug (x, ki, .. , kj, x), d. h. keine Knotenzweige mit
gleichem Anfangs- und Endknoten
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
TERMINOLOGIE
Wurzelbäume sind Bäume mit einem als Wurzel ausgezeichneten Knoten
Festlegung von c als Wurzel Darstellung verändert
Knoten x ist Vorgänger von y gdw- x liegt auf Knotenzug von der Wurzel zu y
Knoten x ist direkter Vorgänger von y gdw. x Vorgänger von y und {x,y} E
(direkter Vorgänger wird auch Elternknote, Vater- /Mutterknoten genannt)
y heißt direkter Nachfolger (Kind) von x in Baum T gdw. x direkter Vorgänger von y
y' heißt Nachfolger von y gdw. y' ist ein Kind von x oder es gibt Knoten y mit y Kind von x und '
Nachfolger von y
DEFINITION VON BLATT UND INNERER KNOTEN
Knoten x heißt Blatt im Baum T, wenn x keine Nachfolger hat, sonst innerer Knoten.
BINÄRE BÄUME
Defintion: Ein Baum wird binärer Baum genannt, wenn jeder Knoten höchstens zwei Kinder hat
Ordnung der Nachfolger von x: linkes Kind, rechtes Kind
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
BINÄRE BÄUME IN HASKELL
data Baum a = Leer | B (Baum a) a (Baum a)
99:
#:
#99:
#+:
#9:
#,:
#
Definition: Sei B binärer Baum min. (K, E)
K = {1, 2, 3, 4, 5}
E = {(1,2), (1,3), (3,4), (3,5)}
Schreiben Sie eine Funktion groesse:: Baum a -> Int
groesse::Baum a -> Int
groesse Leer = 0
groesse (B l _ r) = groesse l + 1 + groesse r
Definition:
Tiefe eines Knotens = Anzahl der Knoten bis zur Wurzel
Höhe eines Baumes = Maximale Tiefe seiner Blätter
Schreiben Sie eine Funktion hoehe:: Baum a -> Int
hoehe::Baum a -> Int
hoehe Leer = 0
hoehe (B Leer _ Leer) = 0
hoehe (B l _ r) = (max (hoehe l) (hoehe r)) + 1
Sei B ein binärer Baum mit Höhe h. Wieviele Knoten hat B (max, min)?
h
2i , h 1
i 0
h
( h 1 % #Knoten % 2 i 2h 1
1
i 0
DURCHLAUFEN DER KNOTEN EINES BAUMES
preorder:
1. Wurzel
2. linker Teilbaum
3. rechter Teilbaum
inorder:
1. linker Teilbaum
2. Wurzel
3. rechter Teilbaum
preorder:
1. linker Teilbaum
2. rechter Teilbaum
3. Wurzel
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
preorder [1, 2, 4, 8, 5, 3, 6, 7]
inorder [2, 4, 8, 5, 1, 3, 6, 7]
postorder [2, 4, 8, 5, 3, 6, 7, 1]
VOLLSTÄNDIGE INDUKTION FÜR BÄUME
Gegeben sei
(mapT.1)
(mapT.2)
mapTree f Leer = Leer
mapTree f (B l e r) = B (mapTree f l) (f e) (mapTree f r)
(col.1)
(col.2)
collapse Leer = []
collapse (B l e r) = (collapse l) ++ [e] ++ (collapse r)
Zeige für alle Bäume b vom Typ (Baum a) map f (collapse b)= collapse (mapTree f b)
Beweis durch vollständige Induktion über die Höhe des Baumes h
Sei b ein Baum vom Typ (Baum b) mit Höhe h 0
I Induktionsanfang: sei h = 0
map f collapse b
map fcollapse Leer map f col.1
map.1
collapse mapTree f b
collapsemapTree f Leer
collapse Leer mapT.1 col.1
II.Induktionsschritt: sei b = (B l e r)
1. Induktionsbehauptung:
map f (collapse (B l e r)) = collapse (mapTree f (B l e r)) unter der
2. Induktionsvorraussetzung dass gilt:
(IV.1) map f (collapse l) = collapse (mapTree f l)
(IV.2) map f (collapse r) = collapse (mapTree f r),
mit der Höhe von l, r ist h 0, bel. fest
Höhe l = h1
Höhe r = h2
h = max (h1, h2)
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
3. Induktionsbeweis
map f collapse B l e r
map f collapse l e collapse r
col.2
map f collapse l
map f e map f collapse r collapse mapTree f l
map f e map f collapse r collapse mapTree f l
map f e collapse mapTree f r
NV IV.1
IV.2
collapse mapTree f l
f e : map f collapse mapTree f r collapse mapTree f l
f e collapse mapTree f r
map.2
map.1
collapse B mapTree f l
f e
mapTree f r
col.2
collapse mapTree f B l e r
mapT.2
Nebenvorraussetzung (eigentlich als Zwischenbeweis aufzuführen):
map f (xs ++ ys) == map f xs ++ map f ys
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
Binäre Suchbäume und ADTs
19.08.2008
BINÄRE SUCHBÄUME
Definition: Ein binärer Suchbaum ist ein binärer Baum mit data Baum a = Leer | B (Bauma) a (Baum a),
wobei Ord a => Baum a und für alle Knoten a aus b gilt:
1. Alle Knoten im linken Teilbaum von a sind < a
2. Alle Knoten im rechten Teilbaum von a sind > a
Und jedes Elemet kommt nur einmal vor
Bsp.: {4, 6, 7, 8, 9}
insTree
insTree
insTree
|
|
|
:: Ord a => a -> Baum a -> Baum a
e Leer = B Leer e Leer
e (B l x r)
e == x = (B l x r)
e < x = (B (insTree e l) x r)
otherwise = B l x (insTree e r)
Entfernen von k im Baum
1. Fall: k ist Blatt in b lösche k
2. Fall: k ist innerer Knoten mit einem Kind lösche k und direkter Vorgänger von k direkter Vorgänger
vom direkten Nachfahren von k
3. Fall: Sonst: k ist innerer Knoten mit zwei Kindern
Suche im linken Teilbaum von k den kleinsten Knoten und füge diesen an die Stelle von k und
„ziehe“ linken Teilbaum von x hoch
ADTS
Problem: Wie soll eine Datenstruktur wie binäre Suchbäume implementiert / definiert werden?
data Baum a = Leer | B (Baum a) a (Baum a) reicht nicht
Lösung: Ein ADT = Abstrakter Datentyp beschreibt eine Datenstruktur mittels Funktionen, die diese Struktur
aufbauen / verändern.
Beispiel: bin. Suchbaum als ADT wird durch Einfüge- und Entfernfunktion definiert.
Sei t ADT mit
einfuegen (x, t) Wobei t eine Struktur die ...
entfernen (x, t)
Wozu ADT
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
ADTs stellen Schnittstelle zu einem konkreten Typen drch Angaben einer Menge von konkreten Operatoren
mit bestimmten Funktionalitäten, den konkreten Typ kann dann nur über freie Operatoren angesprochen
werden.
module binSearchTree (Baum, einfuegen, entfernen) where -- Def vom ADT
...
data Baum a = ...
-- Umsetzung
einfuegen ...
entfernen ...
QUEUE
Queue Q ist ein ADT mit :
mit x wird so eingefügt und entfernt,
enqueue (x, Q)
dass im Verhältnis zu den anderen Elementen
dequeue (Q)
eine Warteschlange realisiert wir
in Haskell
module Queue
(Queue ,
emptyQ ,
isEmptyQ ,
addQ ,
remQ
) where
1. Möglichkeit der Umsetzung
data Queue a = Qu [a]
add x x (Qu xs) = Qu (xs ++ [x])
--> O(n) wegen n (:)-Operationen
remQ (Qu xs) | not(isEmptyQ (Qu xs)) = (head xs, Qu (tail xs)) --> O(1), c=5
| otherwise = error "Queue ist voll"
MENGE
Eine Menge (set) ist ADT mit
bei Mengen ist keine Aufzählung der Elemente vorgegeben
jedes Element ist nur einmal in er Menge enthalten
A ist eine Teilmenge von B = A B
A vereint mit B = A B
A ohne B = A B = {a: a A a B}
Durchschnitt von A und B = A B = a: a A a B}
{1,2,3} {2,1,3}
{2,2,2} = {2}
subSet
diff
inter
Sei M Menge: |M| Mächtigkeit von M, d. h. # der Elemente card
Sei M eine Menge:
P(M) = Potrenzmenge von M = Menge, die alle Untermengen von M enthält
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
Beispiel: sei M = {1, 2, 3}; |M| = 3
P(M) = {{},{1},{2},{3},{1,2},{1,3},{2,3},{1,2,3}}
|P(M)| = 8 = 2³
M = {1,2,3,4}; |M| = 4
|P(M)| = 16 = 2
Sei M Menge mit |M| = n, n Zeige |P(M)| = 2, n M = {}
P(M) = {{}}
M = {1}
P(M) = {{},{1}}
M = {1,2}
P(M) = {{},{1},{2},{1,2}}
M = {1,2,3}
P(M) = {{},{1},{2},{3},{1,2},{1,3},{2,3},{1,2,3}}
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
Berechenbarkeit
21.08.2008
PRIMITIV-REKURSIVE FUNKTIONEN
Definition: Die Klasse P der primitiv-rekursiven Funktionen über ist.
(i) Für alle n 0, ist die konstante Funktion cn : n0 ( 0 mit cn x
0, n 0 in P .
(ii) Für alle n 0 und 1 ≤ i ≤ n ist die Projektion pni : n ( 0 mitpni x1 ,.. , xn xi in P .
(iii) Die Funktion N : 0 *0 mit N(x) = x + 1 ist in P.
(iv) Sind die Funktion f :n0 ( 0 und g 1 ,.. ,g n : n0 ( 0 in P,
dann auch h :n0 ( 0 mit h(x) = f (g1(x), .. , gn(x)) in P. h entsteht durch Einsetzung aller
Substitutionen der gi in f.
n
n2
(v) Sind die Funktionen g :0 ( 0 und h : 0 ( 0 in P, dann ist auch jede Funktion
f :n1
( 0 in P, welche die folgende Gleichung erfüllt:
0
f 0, y g y
y n0 (Rekursionsanker)
f x 1, y
h x , f
x , y , y , x 0 y 0
n.
Rekursiver Aufruf
f entsteht aus g und h durch primitive Rekusion
Sei f Funktion mit f : 20 ( 0 , f(x,y) = x+y.
Behauptung: f ist berechenbar (hier primitiv-rekursiv, also in P)
nach (ii)
g x
p 1n x
x , x 0
3
3
nach (ii, iii) h x 1 , x2 , x3 p2 x 1 , x 2 1 , x 3 x1 , x2 , x 3 0
nach (iv) f 0, y g y
nach (v)
f x 1, y h x , f x , y , y Zeigen Sie, dass f mit f : 20 ( 0
f(x,y) = x · y in P ist
2·5=5+5
5 + 5 = ((((5 + 1) + 1) + 1) + 1) + 1
nach (ii)
g x
c x
0, x 0
3
3
nach (ii, Add.) h x 1 , x 2 , x3 p2 x1 , x2 x3 , x 3 , x1 , x2 , x3 0
nach (iv)
f 0, y
g y
nach (iv)
f x ,0
g x
nach (v)
f x 1 , y h x , f x , y
, y
3
5
2
5
f (2,5) + 5 = (f(1,5) + 5) + 5 = ((g(x) + 5) + 5) + 5 = ((0 + 5) + 5) + 5 = 15
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Autor: Martin Lenders
DIE ACKERMANN-FUNKTION IN DER PETER-DARSTELLUNG
ack (0,y) = y +1
ack(x+1,0) = ack (x,1)
ack(x+1,y+1) = ack(x,ack(x+1,y))
Bsp:
ack (1,1) = ack (0,ack(0+1,0))
= ack (0,ack(0,1))
= ack (0,2) = 3
ack(2,2) = 7
ack(3,3) = 61
ack(4,4) = Zahl mit 19.729 Stellen
ack(4,4) >
10
10
10 19.000
ack ist berechenbar, aber nicht in P
P bildet nicht alle berechenbaren Funktionen ab.
DER μ-OPERATOR
Definition: Seien f, g Funktionen mit f : k0 1 ( 0 , g :k ( min M f , x1 , .. , x k , wenn M f , x1 , .. , x k sonst, undefiniert
n : M f , x1 , .. , xk : n # 0 f x1 , .. , xk , n
0 m ' n : f x 1 , .. , x k , n
ist definiert μ g x1 , .. , xk z. B.:
Eingabe x1, .. , xk
Setze n auf 0
Solange f (x1, .. , xk, n) ≠ 0
erhöhe n um 1
PARTIELLE FUNKTIONEN
f: A B
wenn es in A Elmente gibt, die in B nicht abbildbar sind, dann ist f partiell
in der Urbildmenge A gibt es einen Teil, der mit f nicht definiert ist
Funktionale Programmierung - Mitschrift von Martin Lenders steht unter einer Creative Commons Namensnennung-Keine kommerzielle
Nutzung-Weitergabe unter gleichen Bedingungen 3.0 Deutschland Lizenz.
Herunterladen