Programmiersprachen Sprachen vs. Paradigmen Fragen: Paradigma: [griechisch] das, Wissenschaftstheorie: von T.S. Kuhn eingeführter Begriff, der die Gesamtheit aller eine wissenschaftliche Disziplin in einem Zeitabschnitt beherrschenden Grundauffassungen (z.B. Raum-, Zeitvorstellungen, methodologische Regeln) bezeichnet und somit festlegt, was als wissenschaftlich befriedigende Lösung angesehen werden kann. • Seit wann gibt es Programmiersprachen? ... • Wieviele Programmiersprachen gab bzw. gibt es? ... (aus: Brockhaus-Wissen, 2004) • Welche davon muss man als INF, ING-INF, . . . kennen? ... c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 1 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), Programmierparadigmen 2 Programmierparadigmen • Ein Programmierparadigma ist das einer Programmiersprache . . . oder Programmiertechnik zugrundeliegende Prinzip. (vgl. http://de.wikipedia.org/wiki/Programmierparadigma) ’klassische’ Programmierparadigmen: • imperativ • funktional • Unterschiedliche Paradigmen führen zu unterschiedlichen Sichten auf Programmierprobleme und damit oft zu unterschiedlichen Lösungen. • logisch • objektorientiert Anmerkung: • konkrete Programmiersprachen sind oft Mischformen (hybrid) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 3 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 4 Programmierparadigmen Programmierparadigmen aktuelle Entwicklungen: Wie eignet man sich die unterschiedlichen Programmierparadigmen am besten an? • agentenorientierte Programmierung • aspektorientierte Programmierung • ... c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 5 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), Programmierparadigmen 6 Programmierparadigmen Wie eignet man sich die unterschiedlichen Programmierparadigmen am besten an? Lernziele: • Denk- und Herangehensweisen • Kennenlernen von Prinzipien und Konzepten • adäquate Programmierstile • Kennenlernen von prototypischen Vertretern, also von konkreten Programmiersprachen, die Prinzipien des Paradigmas besonders klar umsetzen • konzeptueller Rahmen für Klassifikation, Bewertung und Aneignung konkreter Programmiersprachen • eigenständiges Lösen von Problemen und Aufgaben • Vergleiche und Diskussion von Alternativen c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 7 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 8 »Warum funktionale Sprachen studieren?« »Warum funktionale Sprachen studieren? « • didaktisches Argument: verbessertes Verständnis dessen, was Programmieren eigentlich ist ganz neue Aspekte gegenüber ‘traditionellen’ Sprachen • praktische Relevanz: Produktivität der Programmierer wird gesteigert, Softwarekosten werden gesenkt (Hinweis: ERLANG von Ericson; XSLT, XQuery ) • theoretische Fundierung: ‘Informatiker zeichnet aus, dass sie das Prinzip des Programmierens und das Prinzip des Sich-in-Programmiersprachen-Ausdrückens beherrschen’ [Pepper 1998] c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 9 »Klassifikation von Programmiersprachen: « [E.W.Dijkstra, 1995] c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 10 »Charakteristika (vgl. [Pepper 1998]) « funktional Programm ist Ein-/Ausgaberelation; diese Abbildung wird im Programmtext als Funktion geschrieben • imperative Programmiersprachen: – ‘klassische’: COBOL, FORTRAN, PASCAL, C, ... – objektorientierte: SMALLTALK, C++, JAVA, ... Programme ‘zeit-los’ • deklarative Programmiersprachen: – logische: PROLOG, ... – funktionale: LISP, SCHEME, ML, MIRANDA, OPAL, GOFER, HASKELL, ... c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), The clarity and economy of expression that the language of functional programming permits is often very impressive, and, but for human inertia, functional programming can be expected to have a brilliant future. 11 Programme auf abstraktem, math. orientiertem Niveau imperativ Programm ist Arbeitsanweisung für Maschine; Ein-/Ausgabeverhalten aus Arbeitsweise der Maschine analysierbar ‘Zustand’ der ausführenden Maschine beeinflusst Programm Programme konkret auf Maschinen bezogen formuliert c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 12 »Haskell « »Programmiersprache Haskell:« • benannt nach Haskell B. Curry Programmierumgebung Hugs • einer der Pioniere des λ-Kalkül • Haskell Users Gofer System • erste Spezifikation der Sprache Ende 80er Jahre • frei erhältlicher Interpreter • aktuelle Version: Haskell 98 • für alle gängigen Plattformen • Download, Tutorials, usw.: http://www.haskell.org/ c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 13 »funktionale Programme in Haskell « c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 14 »funktionale Programme in Haskell cont. « • Definitionen von Funktionen und anderen Werten durch Gleichungen • lies ‘::’ als ‘hat Typ’ oder ‘ist vom Typ’ • Beispiele: • Definition assoziiert Namen (Identifikator) mit Wert eines bestimmten Typs size :: Int size = 12 + 13 • Syntax: square :: Int -> Int square n = n * n <name> :: <type> <name> = <expression> c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 15 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 16 zwei Arten von Dateistilen: zwei Arten von Dateistilen: cont • Skripte (Extension ‘.hs’): • literate Skripte (Extension ‘.lhs’): alles ist Programmtext, sofern nicht explizit als Kommentar gekennzeichnet – Kommentare bis Zeilenende eingeleitet durch zwei aufeinanderfolgende ‘-’ – alles ist Kommentar, sofern nicht am Zeilenanfang durch ‘>’ als Programmzeile gekennzeichnet – literat . . . ‘wörtlich’ – Abschnittskommentare zwischen ‘{-’ und ‘-}’ c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 17 Beispiel eines Skripts c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 18 Beispiel eines literaten Skripts {- Die Berechnung der Funktion Fakultät ist ein Standardbeispiel fuer Rekursion. ... mehrere Zeilen Kommentartext ... -} -- Berechnung der Fakultät mit Konditional if > fak :: Int -> Int > fak n = if n == 0 then 1 else n * fak (n - 1) fak :: Int -> Int Eine Variante mit Pattern-Matching: fak n = if n == 0 then 1 else n * fak (n - 1) > fak 0 = 1 > fak n = n * fak (n - 1) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 19 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 20 Haskell – Sprachelemente Haskell – Sprachelemente • vordefinierte elementare Typen (auch Sorten genannt) für Konstante (= nullstellige Funktionen): • Typisierung: jedes Objekt in Haskell hat einen wohldefinierten Typ • Zweck der Typisierung: Bool Int Char Float Integer Rational Double – frühzeitiges Erkennen von Programmierfehlern (type checking) – schon vor Programmausführung (statische Analyse) • durch Deklaration mit type lassen sich benutzerdefinierte Typen einführen c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 21 Zur Unterscheidung zwischen Int und Integer c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 22 Typisierung cont. • Zur Klasse Int gehören ganze Zahlen, die sich mit einer festen Zahl von Bytes darstellen lassen. • Typ von Funktionen (auch Funktionalität genannt) : Definitions- und Wertebereich durch -> getrennt angegeben • der Wert der Variablen maxBound::Int gibt die grösste als Int darstellbare ganze Zahl an. Dieser Wert ist 2147483647. • Beispiel: double :: Int -> Int double n = 2*n • Will man beliebig grosse ganze Zahlen verarbeiten, so sollte man den Typ Integer verwenden. • bei mehreren Argumenten werden deren Typen durch -> verbunden • Beispiel: max mit 2 Argumenten aus Int und Wert aus Int max :: Int -> Int -> Int c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 23 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 24 Typisierung cont. vordefinierte arithmetische Operatoren • Interpretation einer Typdeklaration wie scale :: Picture -> Int -> Picture • + . . . Summe zweier Zahlen • * . . . Produkt zweier Zahlen • erstens: scale hat zwei Argumente: das erste ist vom Typ Picture, das zweite vom Typ Int • ˆ . . . Exponentiation: 2 ˆ 3 gibt 8 • zweitens: das Ergebnis der Anwendung von scale ist vom Typ Picture • - ... – Differenz, wenn infix verwendet; – umgekehrtes Vorzeichen bei Präfixverwendung (vgl. negate) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 25 vordefinierte arithmetische Operatoren cont. c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 26 vordefinierte Vergleichsoperatoren • div . . . ganzzahlige Division • für ganze Zahlen, d.h. Typ Int -> Int -> Bool: >, >=, ==, / =, <=, < • mod . . . Rest bei ganzzahliger Division (modulo) • abs . . . Absolutbetrag • diese Vergleichsoperatoren sind – wie auch die arithmetischen Operatoren – ‘überladen’ und auch auf Float anwendbar • negate . . . ändere Vorzeichen • Typ dann: Float -> Float -> Bool • für == gilt auch Bool -> Bool -> Bool bzw. sogar allgemein t -> t -> Bool, sofern für den Typ t Gleichheit definiert (Hinweis: t hier Typvariable) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 27 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 28 einige vordefinierte Operatoren bzw. Konstanten für Float Name(n) + - * / ˆ ** exp log logBase pi signum sqrt cos, sin, tan acos, asin, atan ceiling, floor, round fromInt Typ Float -> Float -> Float Float -> Int -> Float Float -> Float -> Float Float -> Float Float -> Float Float -> Float -> Float Float Float -> Float Float -> Float Float -> Float Float -> Float Float -> Int Int -> Float Bem. • werden infix verwendet, d.h. 3 + 4 aber: Verwendung eines Operatorsymbols <op> in Präfixposition möglich mit Notation (<op>) , d.h. (+) 3 4 == 3 + 4 xn xy ex ln x loga x π • können assoziativ sein; z.B. +, * • nicht-assoziative Operatoren werden festgelegt als links-assoziativ oder rechts-assoziativ z.B. - links-assoziativ, d.h. a - b - c == (a - b) - c Rundung Konversion c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), Operatoren 29 Operatoren und Funktionen • Operatoren haben Bindungsstärke oder Fixität (engl. fixity) z.B. * hat Fixität 7, + hat 6, ˆ hat 8, daher a + b * c == a + (b * c) und a ˆ b * c == (a ˆ b) * c c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 30 Konversionen von Operatoren und Funktionen • Funktionsanwendung hat höchste Bindungsstärke • allgemeine Schreibweise: Funktionsname vor Argumente(e) f v1 v2 ...vn • Beachte: da Funktionsanwendung höhere Bindung als jeder andere Operator wird f n+1 interpretiert als (f n)+1 • werden Infix-Operatoren in Klammern eingeschlossen, so können sie als Funktionen vor ihren Argumenten verwendet werden Beispiel: (+) :: Int -> Int -> Int Verwendung: (+) a b == a + b • Funktionen können zu Operatoren gemacht werden durch Einschluss des Funktionsnamen in sog. Backquotes • für andere Interpretation ist explizite Klammerung notwendig: f (n+1) a ‘max‘ b == max a b c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 31 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 32 Funktionen in Haskell: Definition von Funktionen • mit Gleichungen • Funktionen sind in funktionalen Programmiersprachen ‘Bürger erster Klasse’ (‘first class citizens’) • dabei bedingte Ausdrücke verwendbar • sie unterscheiden sich nicht von Daten, max :: Int -> Int -> Int max x y = if x >= y then x else y • sie können Argumente von Funktionen sein, • gegebenenfalls verschachtelte bedingte Ausdrücke • sie können Werte von Funktionen sein, • sie können Elemente in zusammengesetzten Datenstrukturen sein (z.B. Listen, Tupel, . . . ). c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 33 fib :: Int -> Int fib x = if x == 0 then 0 else if x == 1 then 1 else fib (x - 1) + fib (x - 2) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), Definition von Funktionen cont. Definition von Funktionen cont. • Variante mit ‘Wächtern’ (engl. ‘guards’) • Beispiele cont.: • boolesche Ausdrücke für Fälle in einer Definition fib fib | | | • idealerweise disjunkt und vollständig abdeckend • Beispiele: fib x | x == 0 || x == 1 | otherwise = x = y c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), :: Int -> Int x x == 0 = 0 x == 1 = 1 x > 1 = fib (x - 1) + fib (x - 2) • Variante: max :: Int -> Int -> Int max x y | x >= y | otherwise 34 35 = x = fib (x - 1) + fib (x - 2) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 36 Definition von Funktionen cont. Definition von Funktionen cont. • Variante mit ‘Pattern matching’ • Variante mit ‘Pattern matching’ • Beispiel: fib :: Int -> Int fib 0 = 0 fib 1 = 1 fib x = fib (x - 1) + fib (x - 2) mistery :: Int -> Int -> Int mistery 0 y = y mistery x y = x • ‘guards’ bzw. Gleichungen werden sequentiell ausgewertet c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 37 Definition von Funktionen cont. 38 Operationen mit Funktionen • Verwendung von ‘wildcards’ in Mustern möglich • Definition von Funktionen • Anwendung von Funktionen auf Argumente mistery :: Int -> Int -> Int – Argumente können verschachtelt wieder Anwendungen von Funktionen auf Argumente sein mistery 0 y = y mistery x _ = x • ‘wildcards’ immer dann sinnvoll, wenn beliebige Werte im Muster (auf der linken Seite) zugelassen werden sollen und auf diese auf der rechten Seite der Gleichung nicht verwiesen werden muss c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 39 • partielle Anwendung von Funktionen • Funktionskomposition c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 40 Haskell – lexikalische Konventionen Haskell – lexikalische Konventionen • Identifikatoren: Bezeichner von Variablen, von Typen von Funktionen usw. • Identifikatoren müssen in Haskell mit einem Buchstaben beginnen • dieser kann von einer beliebigen Sequenz von Buchstaben, Ziffern, Unterstrichen ( ) und einzelnen Anführungszeichen gefolgt sein Konventionen für führende Großbuchstaben bzw. Kleinbuchstaben • alle Namen, die in Definitionen von Werten verwendet werden (d.h. also auch alle Funktionsnamen), müssen mit einem kleinen Buchstaben beginnen, ebenso die Bezeichner für Variable und Typvariable • Beachte: Verwendung von – einzeln: . . . – als erstes Zeichen gefolgt von Sequenz: . . . • mit einem Großbuchstaben beginnen Typnamen (z.B. Int), Konstruktoren, True und False, Modulnamen und auch die Namen von Typklassen c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 41 Haskell – lexikalische Konventionen 42 Haskell – lexikalische Konventionen kleine Sammlung sog. reservierter Wörter • Empfehlung: mnemonische, ’sprechende’ Namen verwenden • können nicht als Identifikatoren benutzt werden • z.B. zinssatz statt nur z • dazu zählen: • Empfehlung: bei Namen, die aus mehreren Worten zusammengesetzt sind, den Beginn des zweiten Worts (und ggf. folgender Wörter) in Großbuchstaben schreiben case|class|data|default|deriving|do|else |if|import|in|infix|infixl|infixr|instance |let|module|newtype|of|then|type|where|_ c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), • z.B. letztesElement 43 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 44 Listen in Haskell: Primitive Rekursion über Listen • Listen . . . Zusammenfassung beliebig vieler Elemente vom selben Typ • auch ‘tail recursion’ genannt • Darstellung des Listentyp: [<typ>], z.B. • Definition durch – Angabe des Ausgangswerts für [] und – Angabe, wie von Wert für xs auf Wert von (x:xs) übergegangen wird [Int], [Char], [[Int]], [(Name, Matrnr)], ... • Listen werden – sofern ungleich der leeren Liste [] - durch Anwendung des Konstruktors ‘:’ in eindeutiger Weise ausgehend von [] aufgebaut • Beispiel: length [] = 0 length (x:xs) = 1 + length xs • Beispiel: [4, 2, 3] == 4:2:3:[] == 4:(2:(3:[])) • ‘:’ ist rechts-assoziativ, d.h. x:y:zs = x:(y:zs) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 45 Primitive Rekursion über Listen c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 46 Rekursion über Listen cont. Beachte: • Beispiel: Summe der Werte der Listenelemente • length ist polymorph, d.h. auf Listen unterschiedlichen Typs anwendbar: length :: [a] -> Int • ’wildcard’ in der zweiten Gleichung der Definition möglich, d.h. length ( :xs) = 1 + length xs sum :: [Int] -> Int sum [] = 0 sum (x:xs) = x + sum xs • Frage: sum [2, 4 .. 11] ֒→ . . . ? • length ist wie viele andere Funktionen auf Listen im sog. StandardPrelude von Haskell bereits vordefiniert • sum ist vordefiniert und polymorph Prelude> :t sum sum :: Num a => [a] -> a c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 47 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 48 Rekursion über Listen cont. Der Typ String Beispiel: Sortieren mit Insertion-Sort • Spezialfall: Liste aus Zeichen, d.h. type String = [Char] iSort :: [Int] -> [Int] • alle polymorphen Listenfunktionen können für String benutzt werden iSort [] iSort (x:xs) = [] = ins x (iSort xs) • Ausgabe mit putStr putStr :: String -> IO () ins :: Int -> [Int] -> [Int] ins ins | | • Wechsel zwischen Strings und Werten: show (2 + 3) ==> "5" x [] = [x] x (y:ys) x<= y = x:(y:ys) otherwise = y: ins x ys c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), • umgekehrt: (read "True") :: Bool ==> True 49 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 50 Darstellung von Listen Darstellung von Listen • für Listen von Zahlen, Zeichen und anderen Aufzählungstypen gibt es Kurzschreibweisen • Variante: die ‘Differenz’ zwischen dem zweiten und dem ersten Element ergibt die ‘Schrittweite’ • [n .. m] Kurzform für [n,n+1,...,m] (falls m kleiner als n ist Liste leer) • Beispiele: [3,5 .. 14] [0.0,0.4 .. 2.0] [’f’,’h’ .. ’q’] • Beispiele: [3 .. 9] [1.2 .. 4.1] [’f’ .. ’q’] = [3,5,7,9,11,13] = [0.0,0.4,0.8,1.2,1.6,2.0] = "fhjlnp" == [3,4,5,6,7,8,9] == [1.2,2.2,3.2] == "fghijklmnopq" c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 51 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 52 Listenkomprehension Listenkomprehension cont.: Beispiele (vgl. [S.Thompson, 1999, pp.79]) • Vorbild: math. Notation für Mengen • Beispiel: Teiler(n) = {i∈Nk i≤n, i Teiler von n} • Sei ex die Liste [2,4,7] • in Haskell: teiler :: Int -> [Int] teiler n = [ i | i<-[1..n], mod n i == 0] • [ 2*n | n<-ex] ֒→ [4,8,14] • lies: ‘Nimm alle 2*n für n aus der Liste ex’ – Liste [1..n] wirkt als Generator – Test mod n i == 0 wählt Elemente aus • zur Veranschaulichung: Auswertung tabellarisch n 2*n • mehrere Tests in Form boolescher Ausdrücke möglich c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 53 = = 2 4 4 8 7 14 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), Listenkomprehension cont.: Beispiele 54 Listenkomprehension cont.: Beispiele (vgl. [S.Thompson, 1999, pp.79]) (vgl. [S.Thompson, 1999, pp.79]) • Sei isEven definiert: • links von <- können nicht nur Variable, sondern auch Muster (pattern) stehen isEven :: Int -> Bool isEven n = (n ‘mod‘ 2 == 0) • Beispiel: addPairs :: [(Int,Int)] -> [Int] addPairs pairList = [ n+m | (n,m) <- pairList] • [ isEven n | n<-ex] ֒→ [True, True, False] • Veranschaulichung mit Tabelle: • mehrere Tests möglich [ n+m n m n+m • [ 2*n | n<-ex, isEven n, n>3] c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 55 | (n,m) <- [(2,3),(2,1),(7,8)]] = 2 2 7 = 3 1 8 = 5 3 15 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 56 Listenkomprehension cont. Listenkomprehension cont.: (vgl. [S.Thompson, 1999, pp.79]) • Syntax: allgemeine Form ist [ e | q1, ... qk ] • zusätzlicher Test: • dabei ist jeder der Qualifikatoren qi • nur geordnete Paare addieren addOrdPairs :: [(Int,Int)] -> [Int] addOrdPairs pairList = [ n+m | (n,m) <- pairList, n < m] • wichtig: ein Ausdruck lExp bzw. bExp in qi kann auf die in q1 bis qi−1 benutzten Variablen verweisen • Frage: addOrdPairs [(2,3),(2,1),(7,8)] ֒→ . . . ? c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), – entweder ein Generator der Form p <- lExp mit p Muster und lExp Ausdruck vom Listentyp – oder ein Test, d.h. boolescher Ausdruck bExp 57 Listenkomprehension cont. c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), Listenkomprehension cont.: (vgl. [S.Thompson, 1999, 17.3]) • pythagoräische Tripel: für welche Tripel (x, y, z) ganzer Zahlen gilt: • mit mehreren Generatoren können Elemente aus zwei oder mehr Listen kombiniert werden x2 + y2 = z2 ? • als Listenkomprehension: • Beispiel: pyTriple n = [(x,y,z)| x<-[2 .. n], y<-[x+1 .. n], z<-[y+1 .. n], x*x+y*y==z*z] pairs :: [a] -> [b] -> [(a,b)] pairs xs ys = [ (x,y) | x<-xs , y<-ys ] • Frage: pyTriple 100 ֒→ ... ? pairs [1,2,3] [4,5] ֒→ [(1,4),(1,5),(2,4),(2,5),(3,4),(3,5)] c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 58 59 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 60 Tupel in Haskell: Tupel in Haskell: • Aggregationstyp Tupel: auch Tupel dienen – wie Listen – zum Zusammenfassen von Daten zu einem Objekt • in Tupeln wird eine feste Anzahl von Objekten aggregiert, die aber von unterschiedlichem Typ sein können • Unterschied: Liste aggregiert variable Anzahl von Objekten mit identischem Typ c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 61 Verwendung von Tupeln (vgl. [S.Thompson, 1999, 5.2]) – Name (Typ String) und – Matrikelnummer (Typ Int) type Student = (String,Int) • math. Sicht: Tupeltyp als Teilmenge des Kreuzprodukts der Komponententypen c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 62 Funktionen mit Tupeln • Tupel für zusammengesetzte Resultate von Funktionen; Beispiel: • meist mit Pattern matching definiert; Beispiel: minAndMax :: Int -> Int -> (Int, Int) minAndMax x y | x>=y | otherwise • Beispiel: Studenten seien dargestellt durch Paare aus addPair :: (Int, Int) -> Int addPair (x,y) = x+y = (y,x) = (x,y) • Variante mit vordefinierten Selektorfunktionen für Paare addPair :: (Int, Int) -> Int addPair p = fst p + snd p c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 63 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 64 Funktionen mit Tupeln Beispiel: effiziente Berechnung der Fibonacci-Zahlen (vgl. [S.Thompson, 1999, pp. 75]) • mögliche Definition dieser Selektorfunktionen: • Idee: Funktion, die zwei aufeinanderfolgende Werte der Folge als Resultat, d.h. gesucht ist fst (x,y) = x snd (x,y) = y fibPair n = (fib n, fib (n+1)) • in Prelude.hs: mit wildcards in Muster fst fst (x,_) :: (a,b) -> a = x snd snd (_,y) :: (a,b) -> b = y c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), • aus einem Paar (u,v) ergibt sich das nachfolgende als (v,u+v) über fibStep fibStep :: (Int, Int) -> (Int, Int) fibStep (u,v) = (v,u+v) 65 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), Beispiel: effiziente Berechnung der Fibonacci-Zahlen cont. Funktionen mit Tupeln 66 bei Definition von Funktionen auf Tupeln können auch verschachtelte Muster verwendet werden (vgl. [S.Thompson, 1999, 5.2]) • damit fibPair definierbar als fibPair :: Int -> (Int, Int) fibPair n | n== 0 = (0,1) | otherwise = fibStep (fibPair (n-1)) shift :: ((Int,Int),Int) -> (Int,(Int,Int)) shift ((x,y),z) = (x,(y,z)) • schliesslich: das erste Element des Paars ist das Ergebnis fastFib :: Int -> Int fastFib = fst . fibPair c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 67 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 68 Funktionale Abstraktion: Beispiele rekursiv definierter Funktionen cont. Beispiele rekursiv definierter Funktionen (vgl. [Rabhi & Lapalme, 1999, 1.2.2]) • Summe der natürlichen Zahlen bis n • Fakultätsfunktion fact n | n == 0 | n > 0 • ... sumInt n | n == 0 | n > 0 = 1 = n * fact(n-1) = 0 = n + sumInt(n-1) • Summe der Quadrate der natürlichen Zahlen bis n • ... sumSqr n | n == 0 | n > 0 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 69 Beispiele . . . cont. = 0 = n*n + sumSqr(n-1) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 70 Darstellung dieser Abstraktion in Haskell das den obigen Beispielen zugrundeliegende Rekursionsprinzip kann wie folgt abstrahiert werden: • es wird Funktionswert für die Basis (d.h. für n == 0) definiert induction base comb n | n == 0 = base | n > 0 = comb n (induction base comb (n-1)) Frage: Typ von induction ? • der Funktionswert für n ergibt sich durch Kombination von n (bzw. eines aus n berechneten Werts) mit dem Funktionswert für (n-1) • m.a.W.: der Rekursionsschritt wird als Anwendung einer zweistelligen Kombinationsfunktion auf n und Funktionswert für (n-1) realisiert c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 71 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 72 Abstraktion: induction Abstraktion: induction • induction ist Funktion höherer Ordnung (nimmt eine Funktion als Argument; hier: comb) • damit Darstellung von fact bzw. sumInt mit Hilfe von induction und der vordefinierten Funktionen (+) bzw. (*) > fact n = induction 1 (*) n > sumInt n = induction 0 (+) n c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 73 Benannte vs. anonyme Funktionen > fact = induction 1 (*) > sumInt = induction 0 (+) • die rechten Seiten dieser Gleichungen zeigen sog. partielle Anwendungen von mehrstelligen Funktionen c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 74 Benannte vs. anonyme Funktionen • die Kombinationsfunktion für sumSqr könnte benannt definiert werden, z.B. f x y = x*x + y • dann: • alternativ: Kombinationsfunktion als sog. anonyme Funktion • Syntax in Anlehnung an den λ-Kalkül mit (λxy.x ∗ x + y): sumSqr n = induction 0 (\x y -> x*x + y) n > sumSqr n = induction 0 f n bzw. > sumSqr = induction 0 f c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), • alternativ: Definition auf Funktionsebene möglich, d.h. bzw. sumSqr = induction 0 (\x y -> x*x + y) 75 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 76 Funktionen höherer Ordnung: Die Funktion map Programmiermuster: Anwendung einer Funktion auf jedes Element einer Liste; Ergebnis ist die Liste der Funktionswerte zu den Elementen Funktionen sind Funktionen höherer Ordnung, wenn sie • eine Funktion als Argument nehmen und/oder mögliche Definitionen: • eine Funktion als Wert zurückgeben. • als Listenkomprehension: Wiederkehrende Programmiermuster (pattern) lassen sich häufig als Funktionen höherer Ordnung abstrahieren. map f xs = [ f x | x <- xs] • direkt: map f [] map f x:xs c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 77 Die Funktion map cont. c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 78 Programmiermuster Filtern: Wiederkehrende Frage: welche Elemente einer Liste genügen einer gewünschten Eigenschaft? • Typ von map map :: (a -> b) -> [a] -> [b] • Darstellung von Eigenschaften als Prädikate, d.h. als Funktionen vom Typ t -> Bool für bel. Typ t • Beispiele: • Funktion filter nimmt ein Prädikat und eine Liste als Argument und gibt die Liste derjenigen Elemente zurück, für die das Prädikat zutrifft doubleAll xs = map double xs double x = 2 * x • mögliche Definition als Listenkomprehension: convertChrs :: [Char] -> [Int] convertChrs xs = map ord xs c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), = [] = f x : map f xs filter p xs = [x | x <- xs, p x] 79 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 80 Programmiermuster Filtern: Elementweises Verbinden von Listen: zip • mögliche Definition direkt: • zip: aus Paar von Listen mache Liste mit Paaren korrespondierender Elemente; ignoriere „überschüssige“ Elemente ohne korrespondierenden Partner filter p [] = [] filter p x:xs = if p x then x:filter p xs else filter p xs • • Typ von filter: • Beispiel: filter :: ( a -> Bool) -> [a] -> [a] c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), zip :: [a] -> [b] -> [(a,b)] zip [4,7,1,1] "Koeln" = [(4,’K’),(7,’o’),(1,’e’),(1,’l’)] 81 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 82 Elementweises Verbinden von Listen: zipWith Anonyme Funktionen • Verallgemeinerung zipWith: verknüpfe die korrespondierenden Elemente mit einer zweistelligen Funktion • Funktionen als Argumente von Funktionen höherer Ordnung können einerseits durch ihren Namen referenziert werden zipWith f (x:xs) (y:ys) = f x y : zipWith f xs ys zipWith f _ _ = [] • wird eine Funktion nur als Argument bei einer Anwendung einer Funktion höherer Ordnung benötigt (und nirgends sonst), so reicht oft auch eine sog.anonyme Funktion aus • Beispiel: • die Darstellung anonymer Funktionen erfolgt durch einen LambdaAusdruck mit der Syntax: (\<Var(s)> -> <Körper>) sideBySide p1 p2 = zipWith (++) p1 p2 • Typ? : zipWith :: • Beispiele: (\x -> x*x) oder (\x y --> x*x - 2*x*y + y*y) ................................... c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 83 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 84 Funktionen in Prelude.hs Funktionen in Prelude.hs Prelude.hs enthält viele nützliche Funktionen Funktionen, die ein Paar als Argument nehmen, vs. Funktionen mit zwei Argumenten /usr/local/hugs98-Dec2001/share/hugs/lib/Prelude.hs c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 85 Funktionen in Prelude.hs cont. id id curry curry f x y :: ((a,b) -> c) -> (a -> b -> c) = f (x,y) uncurry uncurry f p :: (a -> b -> c) -> ((a,b) -> c) = f (fst p) (snd p) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), Listenfunktionen: Standard list functions (PreludeList) :: a -> a = x head head (x:_) :: [a] -> a = x const const k _ :: a -> b -> a = k last last [x] last (_:xs) :: [a] -> a = x = last xs (.) (f . g) x :: (b -> c) -> (a -> b) -> (a -> c) = f (g x) tail tail (_:xs) :: [a] -> [a] = xs flip flip f x y :: (a -> b -> c) -> b -> a -> c = f y x init init [x] init (x:xs) :: [a] -> [a] = [] = x : init xs x c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 86 87 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 88 Listenfunktionen cont. null null [] null (_:_) :: [a] -> Bool = True = False (++) [] ++ ys (x:xs) ++ ys :: [a] -> [a] -> [a] = ys = x : (xs ++ ys) map map f xs :: (a -> b) -> [a] -> [b] = [ f x | x <- xs ] filter filter p xs :: (a -> Bool) -> [a] -> [a] = [ x | x <- xs, p x ] c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), Listenfunktionen cont. concat concat :: [[a]] -> [a] = foldr (++) [] length length :: [a] -> Int = foldl’ (\n _ -> n + 1) 0 (!!) (x:_) (_:xs) (_:_) [] 89 !! !! !! !! :: [a] -> Int -> a 0 = x n | n>0 = xs !! (n-1) _ = error "Prelude.!!: negative index" _ = error "Prelude.!!: index too large" c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), Funktionen in Prelude.hs cont.: Faltung 90 Funktionen in Prelude.hs cont. Fehlererzeugung: foldl :: (a -> b -> a) -> a -> [b] -> a foldl f z [] = z foldl f z (x:xs) = foldl f (f z x) xs primitive error foldr :: (a -> b -> b) -> b -> [a] -> b foldr f z [] = z foldr f z (x:xs) = f x (foldr f z xs) Beispiel einer Kontrollstruktur: until until p f x ... c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), :: String -> a 91 :: (a -> Bool) -> (a -> a) -> a -> a = if p x then x else until p f (f x) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 92 Prinzip der strukturellen Induktion für Listen: Beispiel für Beweis einer Programmeigenschaft um zu beweisen, dass eine logische Eigenschaft P(xs) für alle endlichen Listen xs gilt, sind zwei Dinge zu tun • Induktionsanfang oder -basis: Beweise P([]) direkt • Induktionsschritt: Beweise P(x:xs) unter der Annahme, dass P(xs) gilt – m.a.W.: es ist zu zeigen, dass P(xs) ⇒ P(x:xs) – dabei: P(xs) . . . Induktionshypothese • Gegeben seien die folgenden Definitionen: sum :: [Int] -> Int sum [] = 0 sum (x:xs) = x + sum xs (sum.1) (sum.2) doubleAll :: [Int] -> [Int] doubleAll [] = [] doubleAll (z:zs) = 2*z : doubleAll zs (doubleAll.1) (doubleAll.2) • Behauptung: für alle endlichen Listen xs ganzer Zahlen gilt: sum (doubleAll xs) = 2*sum xs c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 93 Beweis einer Programmeigenschaft cont. • Behauptung: • Induktionsbasis: Beweise, dass length (xs ++ ys) = 2 * sum[] (base) • Induktionsschritt: Beweise, dass sum (doubleAll (x:xs)) = 2 * sum (x:xs) (ind) = length xs + length ys • Dabei gelten die folgenden Definitionen: length [] length (z:zs) = 0 = 1 + length zs [] ++ zs = zs (w:ws) ++ zs = w : (ws ++ zs) unter der Induktionshypothese sum (doubleAll xs) 94 weitere Beispiele für Induktionsbeweise Struktur des Beweises: sum (doubleAll []) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), = 2 * sum xs c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), (length.1) (length.2) (++.1) (++.2) (hyp) 95 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 96 weitere Induktionsbeweise cont. weitere Induktionsbeweise cont. Struktur des Beweises: • Sei reverse definiert durch: • Induktionsbasis: Beweise, dass length ([] ++ ys) = length [] + length ys (base) reverse [] reverse (z:zs) = [] = reverse zs ++ [z] (reverse.1) (reverse.2) • Behauptung: für alle endlichen Listen xs und ys gilt • Induktionsschritt: Beweise, dass length ((x:xs) ++ ys) = length (x:xs) + length ys (ind) reverse (xs ++ ys) = reverse ys ++ reverse xs (reverse++) unter der Induktionshypothese length (xs ++ ys) = length xs + length ys (hyp) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 97 weitere Induktionsbeweise cont. c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 98 Ausführung des Beweises Struktur des Beweises für Beziehung (reverse++): • Induktionsbasis: Beweise, dass .................................................... • Induktionsschritt: Beweise, dass .................................................... unter der Induktionshypothese .................................................... c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 99 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 100 Funktionen höherer Ordnung: Falten einer Funktion f cont. Falten einer Funktion f in eine nichtleere Liste In Haskell: Fälle: foldr1 f [x] • Falten von f in eine einelementige Liste [x] ergibt x foldr1 f (x:xs) = f x (foldr1 f xs) = x Typ: • Falten von f in eine längere Liste entspricht foldr1 :: (a -> a -> a) -> [a] -> a foldr1 f [e1, e2, ... , ek] = e1 ‘f‘ (e2 ‘f‘ ( ... ‘f‘ ek) ... ) = e1 ‘f‘ (foldr1 f [e2, ... , ek]) = f e1 (foldr1 f [e2, ... , ek]) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 101 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), Falten einer Funktion f cont. Falten einer Funktion f cont. Beispiele: foldr1 (+) [4,7,1,1] = 13 foldr1 (||) [False, True, False] = True Verallgemeinerung: durch Angabe eines Werts s für die leere Liste lässt sich eine für alle endlichen Listen anwendbare Faltungsfunktion definieren foldr1 (++) ["Dies ", "ist ", "ein ", "Beispiel!"] = "Dies ist ein Beispiel!" foldr1 max [4,7,1,1] = 7 foldr1 min [4,7,1,1] = 1 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 102 103 foldr f s [] = s foldr f s x:xs = f x (foldr f s xs) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 104 Falten einer Funktion f cont. Beispiele für mit foldr definierbare Standardfunktionen von Haskell Typ von foldr: concat :: [[a]] -> [a] concat xs = foldr (++) [] xs foldr :: (a -> a -> a) -> a -> [a] -> a and :: [Bool] -> Bool and bs = foldr (&&) True bs falls Startwert von Typ b: foldr :: (a -> b -> b) -> b -> [a] -> b rev :: [a] -> [a] rev xs = foldr snoc [] xs snoc :: a -> [a] -> [a] snoc x xs = xs ++ [x] c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 105 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), foldr1 vs. foldr Beispiele . . . cont. Frage: Lässt sich foldr1 mit foldr definieren? Wenn ja, wie und unter welchen Bedingungen an f? iSort :: [Int] -> [Int] iSort xs = foldr ins [] xs ins ins | | 106 foldr1 x [] = [x] x (y:ys) x<= y = x:(y:ys) otherwise = y: ins x ys ................................... ................................... ................................... c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 107 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 108 Funktionen als Werte: Funktionen als Werte: Ein ungewohnter Gedanke: . . . mit weitreichenden Konsequenzen: . . . I was extremely ill-equipped to appreciate functional programming when I encountered it: I was, for instance, totally baffled by the shocking suggestion that the value of a function could be another function. . . . Die Möglichkeit, Funktionen auf vielfältige Weise mittels anderer Funktionen miteinander zu verknüpfen, eröffnet Programmiermethoden, die in ihrer Eleganz und Ausdrucksstärke weit über das hinausgehen, was uns aus der traditionellen imperativen Programmierung vertraut ist. Man kann mit Fug und Recht sagen, dass erst diese Techniken die funktionale Programmierung zu dem machen, was sie ist: eine extrem elegante Form, Algorithmen auszudrücken. [E.W. Dijkstra; zitiert in: Pepper 1999] [Pepper 1999, p.89] c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 109 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 110 Aber: Daher: . . . Dieser Programmierstil ist für viele ungewohnt, und weil Unvertrautes erst einmal verunsichert, ist die erste Reaktion häufig Ablehnung. . . . Und erhöhte Abstraktion geht grundsätzlich mit erhöhter intellektueller Herausforderung einher. Etwas sarkastisch ausgedrückt: Wo man sich in C oder BASIC mit fleißigem Testen und Debugging irgendwie zum Ziel durchwursteln kann, muss man hier nachdenken. Und erstaunlicherweise findet manch einer Letzteres abschreckender als Ersteres. . . . ein objektives Problem: Auf dieser Ebene (mehr oder weniger) virtuos mit Funktionen zu spielen, erhöht zweifellos den Abstraktionsgrad, auf dem Problemlösen stattfindet. [Pepper 1999, p.90] [Pepper 1999, p.90] c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 111 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 112 Funktionen als Werte: Funktionen als Werte: cont. mirror :: (Float -> Float) -> (Float -> Float) Beispiel: Manipulation beliebiger reeller Funktionen (vgl. [Pepper 1999, 8.2.2]) mirror f x = f (-x) Sei f :: Float -> Float beliebig. stretch :: Float -> (Float -> Float) -> (Float -> Float) Dann: stretch r f x = f (x/r) shift :: Float -> (Float -> Float) -> (Float -> Float) Fragen: shift dx f x = f (x - dx) • Wie wirken sich shift, mirror und stretch aus? ... • Wie lassen sie sich als Funktionskomposition darstellen? c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 113 Funktionskomposition c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 114 Funktionskomposition: Variante Vorwärtskomposition Verknüpfung von Funktionen als Kontrollstruktur: f . g bedeutet: wende zuerst g, dann f an; m.a.W. Verknüpfung muss von rechts nach links gelesen werden als ‘g, dann f’ Ausgabe einer Funktion wird Eingabe der nachfolgenden Definition: Definition eines Operators >.>, der Funktionen in der Reihenfolge von links nach rechts verknüpft: (f . g) x = f ( g x ) Typ von ‘.‘: infixl 9 >.> (.) :: (b -> c) -> (a -> b) -> (a -> c) (>.>) :: (a -> b) -> (b -> c) -> (a -> c) Funktionskomposition ist assoziativ, d.h. für alle f, g und h g >.> f = f . g f . (g . h) = (f . g) . h z.B. bei Pictures.hs: rotate = flipH >.> flipV in Haskell aus technischen Gründen als rechtsassoziativ behandelt c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 115 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 116 Funktionskomposition: eine Funktion zweimal anwenden Funktionskomposition Verallgemeinerung: n-fach wiederholte Funktionsanwendung twice :: (a -> a) -> (a -> a) twice f = f . f iter :: Int -> (a -> a) -> (a -> a) iter n f | n > 0 = f . iter (n-1) f |otherwise = id Sei succ :: Int -> Int succ n = n + 1 Frage: Was ergibt iter n double 1 ==> ... ? Was ergibt dann (twice succ) 7 ==> ... ? [Bem.: Variante: Mit Faltung definierbar als iter n f = foldr (.) id (replicate n f) ] c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 117 Partielle Anwendung c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 118 Partielle Anwendung cont. Beispiel: zwei partielle Anwendungen: multiply :: Int -> Int -> Int multiply x y = x * y • multiply 2 :: Int -> Int • map (multiply 2) :: [Int] -> [Int] Frage: was ergibt multiply 2 ==> ... ? Variante ohne partielle Anwendung von map: doubleAll xs = map (multiply 2) xs Beispiel: doubleAll :: [Int] -> [Int] doubleAll = map (multiply 2) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 119 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 120 Bestimmung des Typs einer partiellen Anwendung Assoziativität Streichungsregel (cancellation rule): Wenn der Typ einer Funktion f t1 -> t2 -> ...tn -> t ist und diese wird angewendet auf e1::t1 , e2::t2 , ..., ek ::tk mit (k<=n), Beachte: dann ergibt sich der Ergebnistyp durch „Streichen“ der Typen t1 bis tk , • -> ist rechtsassoziativ, d.h. a -> b -> c entspricht a -> (b -> c) d.h. der Ergebnistyp von f e1 e2 ...ek ist • -> ist nicht assoziativ; Beispiel: • Funktionsanwendung ist linksassoziativ, d.h. f x y = (f x) y f x y /= f (x y) tk+1 -> tk+2 -> ...tn -> t c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), g :: (Int -> Int) -> Int g h = (h 0) + (h 1) 121 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), Funktionen in Haskell Funktionen in Haskell Beachte: jede Funktion in Haskell nimmt exakt ein Argument Allgemein: f e1 e2 ...ek bzw. t1 -> t2 -> ...tn -> t So bedeutet multiply :: Int -> Int -> Int wegen der Rechtsassoziativität multiply :: Int -> (Int -> Int) Damit multiply 2 :: Int -> Int und (multiply 2) 5 :: Int c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 122 sind Abkürzungen für (...((f e1) e2 ) ...ek ) bzw. t1 -> (t2 -> (...(tn -> t) ...)) 123 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 124 Schlussfolgern über Programme: Schlussfolgern über Programme Beweise auf Funktionsebene (vgl. [S.Thompson, 1999, 10.9]) • Prinzip der Extensionalität: zwei Funktionen f und g sind gleich, wenn sie für jedes Argument jeweils den selben Wert haben Beispiel: f . id = f (compId) • Kontrast: Prinzip der Intensionalität: zwei Funktionen f und g sind nur gleich, wenn sie die selben Definitionen haben Beweis: für bel. Argument x gilt (f . id) x = f (id x) = f x (Def. . ) (Def. id) m.a.W. von ihrer Wirkung her sind die Funktionen f . id und f für alle x identisch c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 125 Schlussfolgern über Programme: Verküpfung zwischen map und Funktionskomposition c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 126 Beispiel cont. • Induktionsbasis: Beweise, dass map (f.g) [] = (map f . map g) [] • Behauptung: für alle endlichen Listen xs gilt: (base) • Induktionsschritt: Beweise, dass map (f.g) xs = (map f . map g) xs (map.3) map (f.g) (x:xs) = (map f . map g) (x:xs) unter der Induktionshypothese Beweis: strukturelle Induktion map (f.g) xs = (map f . map g) xs • verfügbare Gleichungen: (hyp) • Durchführung des Beweises: map f [] = [] map f (x:xs) = f x : map f xs (map.1) (map.2) (f.g) x (comp.1) = f (g x) (ind) ................................................................ ................................................................ ................................................................ • ... c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 127 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 128 weiteres Beispiel: Programmtransformation Beweistechnik: Gegenbeispiel • Behauptung: filter p . map f = map f . filter (p.f) • in [S.Thompson, 1999, p. 162] findet sich – ohne Bedingungen an die Funktion f zu formulieren – die folgende Behauptung: . . . We can also define foldr1 from foldr, thus • Beispiel für Anwendung: filter (0<) . map (+1) = map (+1) . filter ((0<).(+1)) = map (+1) . filter ((0<=)) foldr1 f (x:xs) = foldr f x xs • Diese Behauptung erscheint zunächst plausibel, denn es gilt u.a.: Prelude> 6 Prelude> 6 Prelude> 120 Prelude> 120 Beweis durch strukturelle Induktion: • Erinnerung: filter p [] = [] filter p (x:xs) | p x = x:filter p xs | otherwise = filter p xs (filter.1) (filter.2) (filter.3) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 129 Beweistechnik: Gegenbeispiel foldr1 (+) [1,2,3] foldr (+) 1 [2,3] foldr1 (*) [1 .. 5] foldr (*) 1 [2 .. 5] c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 130 Beispiel: foldr1 und foldr Gegenbeispiele machen deutlich, dass (foldr1.0) zumindest nicht für alle (beliebigen) Funktionen f gilt: Prelude> "abcd" Prelude> "bcda" Prelude> 8 Prelude> 9 (foldr1.0) • Behauptung: für alle endlichen Listen xs und assoziative und kommutative Funktionen f gilt: foldr1 f (x:xs) foldr1 (++) ["a","b","c","d"] = foldr f x xs • Struktur des Beweises: • Induktionsbasis: Beweise, dass foldr (++) "a" ["b","c","d"] .................................................... foldr1 (^) [2,3] foldr • Induktionsschritt: Beweise, dass (^) 2 [3] .................................................... unter der Induktionshypothese .................................................... Frage: Welche Forderungen muss f erfüllen, damit (foldr1.0) gilt? c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 131 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 249 Algebraische Typen Produkttypen als algebraische Typen Aufzählungstypen (vgl. [S.Thompson, 1999, 14.1]) • Beispiel: • Beispiel: data People = Person Name Age type Name = String type Age = Int data Temp = Cold | Hot data Season = Spring | Summer | Autumn | Winter • Pattern matching zur Definition von Funktionen weather :: Season -> Temp weather Summer = Hot weather _ = Cold • Beispiele: Person "Hans Mueller" 42 Person "Bart Simpson" 12 • Definition von Gleichheit für Temp Cold == Hot == _ == Cold Hot _ = True = True = False c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 250 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), Produkttypen als algebraische Typen cont. 251 Rekursive algebraische Typen • Pattern matching zur Definition von Funktionen • Beispiel: arithmetische Ausdrücke mit ganzen Zahlen showPerson :: People -> String showPerson (Person st n) = st ++ " -- " ++ show n data Expr = Lit Int| Add Expr Expr| Sub Expr Expr • Konstruktor Person ist Funktion vom Typ • Beispiele: Person :: Name -> Age -> People 7 4+7 (7-4)+11 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 252 Lit 7 Add (Lit 4) (Lit 7) Add (Sub (Lit 7)(Lit 4)) (Lit 11) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 253 Rekursive algebraische Typen cont. Rekursive algebraische Typen cont. • Beispiel: Bäume mit ganzen Zahlen • Funktionen mit Hilfe primitiver Rekursion z.B. Auswerten data NTree = eval :: Expr -> Int eval (Lit n) eval (Add e1 e2) eval (Sub e1 e2) NilT | Node Int NTree NTree • Beispiele für Funktionen: = n = (eval e1) + (eval e2) = (eval e1) - (eval e2) sumTree, depth :: NTree -> Int sumTree NilT = 0 sumTree (Node n t1 t2) = n + sumTree t1 + sumTree t2 ... c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 254 Beispiele für Funktionen: cont. c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 255 Notizen ... depth NilT = 0 depth (Node n t1 t2) = 1 + max (depth t1)(depth t2) occurs :: NTree -> Int -> Int occurs NilT p = 0 occurs (Node n t1 t2) p | n==p = 1 + occurs t1 p + occurs t2 p | otherwise = occurs t1 p + occurs t2 p c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 256 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 257 Polymorphe algebraische Typen: Binäre Bäume als polymorphe algebraische Typen Definitionen algebraischer Typen können Typvariable a, b, ... enthalten (vgl. [S.Thompson, 1999, 14.3]) • die Elemente in den Knoten können von bel. Typ sein • Beispiel: • Beispiel: data Pairs a = Pr a a data Tree a = Nil | Node a (Tree a) (Tree a) deriving (Eq, Ord, Show, Read) • einige Ausprägungen: Pr 47 11 :: Pairs Int Pr [] [42] :: Pairs [Int] Pr [] [] :: Pairs [a] • Beispiel: • der eingebaute Listentyp könnte wie folgt definiert sein: depth :: Tree a -> Int depth Nil = 0 depth (Node n t1 t2) = 1 + max (depth t1)(depth t2) data List a = NilList | Cons a (List a) deriving (Eq, Ord, Show, Read) c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 258 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), Binäre Bäume . . . Binäre Bäume . . . • aus Baum Liste ‘kollabieren’ bei Inorder-Traversierung collapse collapse collapse = 259 • mapTree als Funktion höherer Ordnung auf binären Bäumen :: Tree a -> [a] Nil = [] (Node n t1 t2) collapse t1 ++ [n] ++ collapse t2 mapTree :: (a -> b) -> Tree a -> Tree b mapTree f Nil = Nil mapTree f (Node n t1 t2) = Node (f n) (mapTree f t1) (mapTree f t2) • Beispiel: collapse (Node 7 (Node 4 Nil Nil) (Node 1 (Node 1 Nil Nil) Nil)) = [4,7,1,1] c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 260 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 261 Algebraische Typen: Algebraische Typen: Variante für Produkttypen gibt es eine alternative Syntax: • Instanzen werden kreiert mithilfe des Konstruktors und der benannten Selektoren >p1 = Person’ {name = "Paul", age = 21} • Beispiel (vgl. Folie 134): • da der Selektor die jeweilige Komponente eindeutig benennt, ist die Reihenfolge der Angaben beliebig data Person’ = Person’ {name :: Name, age :: Age} deriving (Eq,Show,Read) >p2 = Person’ {age = 12, name = "Pauline"} • die Ausgabe durch die abgeleitete show-Funktion erfolgt aber in der Reihenfolge aus der data-Definition type Name = String type Age = Int • m.a.W.: statt einer ’positionalen’ Kodierung der Komponenten eines Produkttyps haben wir hier eine Kodierung mit benannten Selektoren c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 262 Main> p1 Person’{name="Paul",age=21} Main> p2 Person’{name="Pauline",age=12} c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 263 Algebraische Typen: Variante Algebraische Typen: Variante • mit den benannten Selektorfunktionen kann auf die Werte der Komponenten zugegriffen werden • beachte: bei der Modifikation wird eine modifizierte Kopie der Ausgangsinstanz kreiert, d.h. diese bleibt unverändert Main> name p1 "Paul" Main> age p2 12 Main> p1 Person’{name="Paul",age=21} • Konstruktor und Selektoren können in Funktionen zur Modifikation von Instanzen verwendet werden • Beispiel: Main> hasBirthday p1 Person’{name="Paul",age=22} Main> p1 Person’{name="Paul",age=21} hasBirthday :: Person’ -> Person’ hasBirthday p = Person’ {name= name p, age= age p + 1} c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 264 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 265 Funktionale Programmierung: Haskell Funktionale Programmierung: Scheme • rein funktional • funktional und imperative Elemente • non-strikt • strikt • strenge Typisierung • keine Typisierung • lazy evaluation • eager evaluation (aber: delay) • Listenkomprehension • – • – • Programm-Daten-Äquivalenz • Pattern matching in Definitionen • – c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 266 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), Rein funktionale Sprachen 267 Funktionskomposition als Sprachmittel • in Scheme: • Programmieren ohne Seiteneffekte (define (compose f1 f2) (lambda (x) (f1 (f2 x)))) • Seiteneffekte können Programme schwer zu lesen und schwer zu kompilieren machen • Abwesenheit von Seiteneffekten: – Ausdrücke sind referentiell transparent – unabhängig von Ausführungsordnung – Denken in Gleichungen möglich – Gleichheit zweier Ausdrücke ist nicht zeitabhängig c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 268 • Beispiel für Anwendung: (define (define (define (define (flatten tree) ...) (remove-duplicates list) ...) list-of-atoms (compose remove-duplicates flatten)) nr-of-atoms (compose length list-of-atoms)) ; 1 ]=> tree ;Value 6: ((a (b (c d) e) (b f g) h (c d))) ; 1 ]=> (list-of-atoms tree) ;Value 7: (a e b f g h c d) ; 1 ]=> (nr-of-atoms tree) ;Value: 8 c Prof. Dr. D. Rösner; erstellt: 3. Juli 2007 Sommer 2007, Programmierparadigmen (PGP), 269