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.