Deklarative (= fortgeschrittene)
Programmierung Vorlesung
WS 09,10; SS 12–14, 16
Johannes Waldmann, HTWK Leipzig 13. Juli 2016
– Typeset by FoilTEX–
Einleitung
Formen der deklarativen Programmierung
•funktionale Programmierung:foldr (+) 0 [1,2,3]
foldr f z l = case l of
[] -> z ; (x:xs) -> f x (foldr f z xs)
•logische Programmierung:append(A,B,[1,2,3]).
append([],YS,YS).
append([X|XS],YS,[X|ZS]):-append(XS,YS,ZS).
•Constraint-Programmierung
(set-logic QF_LIA) (set-option :produce-models true) (declare-fun a () Int) (declare-fun b () Int) (assert (and (>= a 5) (<= b 30) (= (+ a b) 20))) (check-sat) (get-value (a b))
– Typeset by FoilTEX– 1
Definition
deklarativ: jedes (Teil-)Programm/Ausdruck hat einenWert (. . . und keine weitere (versteckte)Wirkung).
Werte k¨onnen sein:
•“klassische” Daten (Zahlen, Listen, B¨aume. . . )
•Funktionen (Sinus, . . . )
•Aktionen (Datei schreiben, . . . )
– Typeset by FoilTEX– 2
Softwaretechnische Vorteile
. . . der deklarativen Programmierung•Beweisbarkeit: Rechnen mit Programmen wie in der Mathematik mit Termen
•Sicherheit: es gibt keine Nebenwirkungen und Wirkungen sieht man bereits am Typ
•Wiederverwendbarkeit: durch Entwurfsmuster (=
Funktionen h¨oherer Ordnung)
•Effizienz: durch Programmtransformationen im Compiler,
•Parallelisierbarkeit: durch Nebenwirkungsfreiheit
– Typeset by FoilTEX– 3
Beispiel Spezifikation/Test
import Test.SmallCheckappend :: forall t . [t] -> [t] -> [t]
append x y = case x of [] -> y
h : t -> h : append t y associative f =
\ x y z -> f x (f y z) == f (f x y) z test1 = smallCheckI
(associative (append::[Int]->[Int]->[Int]))
¨Ubung: Kommutativit¨at (formulieren und testen)
– Typeset by FoilTEX– 4
Beispiel Verifikation
app :: forall t . [t] -> [t] -> [t]app x y = case x of [] -> y
h : t -> h : app t y Beweise
app x (app y z) == app (app x y) z Beweismethode: Induktion nachx.
•Induktionsanfang:x == []. . .
•Induktionsschritt:x == h : t. . .
– Typeset by FoilTEX– 5
Beispiel Parallelisierung (Haskell)
Klassische Implementierung von Mergesort sort :: Ord a => [a] -> [a]sort [] = [] ; sort [x] = [x]
sort xs = let ( left,right ) = split xs sleft = sort left
sright = sort right in merge sleft sright wird parallelisiert durchAnnotationen:
sleft = sort left
‘using‘ rpar ‘dot‘ spineList sright = sort right ‘using‘ spineList vgl.http://thread.gmane.org/gmane.comp.lang.
haskell.parallel/181/focus=202
– Typeset by Foil – 6
Beispiel Parallelisierung (C#, PLINQ)
•Die Anzahl der 1-Bits einer nichtnegativen Zahl:
Func<int,int>f =
x=>{int s=0; while(x>0){s+=x%2;x/=2;}return s;}
•
2X26−1 x=0
f(x) Enumerable.Range(0,1<<26).Select(f).Sum()
•automatische parallele Auswertung, Laufzeitvergleich:
Time(()=>Enumerable.Range(0,1<<26).Select(f).Sum()) Time(()=>Enumerable.Range(0,1<<26).AsParallel()
.Select(f).Sum())
vgl.Introduction to PLINQhttps://msdn.microsoft.
com/en-us/library/dd997425(v=vs.110).aspx
– Typeset by Foil – 7
Softwaretechnische Vorteile
. . . der statischen TypisierungThe language in which you write profoundly affects the design of programs written in that language.
For example, in the OO world, many people use UML to sketch a design. In Haskell or ML, one writes type signatures instead. Much of the initial design phase of a functional program consists of writing type definitions.
Unlike UML, though, all this design is incorporated in the final product, and is machine-checked throughout.
Simon Peyton Jones, in: Masterminds of Programing, 2009;
http://shop.oreilly.com/product/9780596515171.do
– Typeset by FoilTEX– 8
Deklarative Programmierung in der Lehre
•funktionale Programmierung: diese Vorlesung
•logische Programmierung: inAngew. K¨unstl. Intell.
•Constraint-Programmierung: als Master-Wahlfach Beziehungen zu weiteren LV: Voraussetzungen
•B¨aume, Terme (Alg.+DS, Grundlagen Theor. Inf.)
•Logik (Grundlagen TI, Softwaretechnik) Anwendungen:
•Softwarepraktikum
•weitere Sprachkonzepte inPrinzipien v.
Programmiersprachen
•Programmverifikation(vorw. f. imperative Programme)
– Typeset by FoilTEX– 9
Konzepte und Sprachen
Funktionale Programmierung ist einKonzept.Realisierungen:
•in prozeduralen Sprachen:
–Unterprogramme als Argumente (in Pascal) –Funktionszeiger (in C)
•in OO-Sprachen: Befehlsobjekte
•Multi-Paradigmen-Sprachen:
–Lambda-Ausdr¨ucke in C#, Scala, Clojure
•funktionale Programmiersprachen (LISP, ML, Haskell) Die Erkenntnisse sind sprachunabh¨angig.
•A good programmer can write LISP in any language.
•Learn Haskell and become a better Java programmer.
– Typeset by FoilTEX– 10
Gliederung der Vorlesung
•Terme, Termersetzungssysteme algebraische Datentypen, Pattern Matching, Persistenz
•Funktionen (polymorph, h¨oherer Ordnung), Lambda-Kalk¨ul, Rekursionsmuster
•Typklassen zur Steuerung der Polymorphie
•Bedarfsauswertung, unendl. Datenstrukturen (Iterator-Muster)
•weitere Entwurfsmuster
•Code-Qualit¨at, Code-Smells, Refactoring
– Typeset by FoilTEX– 11
Softwaretechnische Aspekte
•algebraische Datentypen, Pattern Matching, Termersetzungssysteme
Scale: case class, Java: Entwurfsmuster Kompositum, immutable objects, das Datenmodell von Git
•Funktionen (h¨oherer Ordnung), Lambda-Kalk¨ul, Rekursionsmuster
Lambda-Ausdr¨ucke in C#, Entwurfsmuster Besucher Codequalit¨at, code smells, Refaktorisierung
•Typklassen zur Steuerung der Polymorphie
Interfaces in Java/C# , automatische Testfallgenerierung
•Bedarfsauswertung, unendl. Datenstrukturen
Iteratoren, Str¨ome, LINQ
– Typeset by FoilTEX– 12
Organisation der LV
•jede Woche eine Vorlesung, eine ¨Ubung
•Hausaufgaben (teilw. autotool)
https://autotool.imn.htwk-leipzig.de/shib/
cgi-bin/Super.cgi
Identifizierung und Authentifizierung ¨uber Shibboleth-IDP des HTWK-Rechenzentrums, wie bei OPAL
•Pr¨ufungszulassung: regelm¨aßiges (d.h. innerhalb der jeweiligen Deadline) und erfolgreiches (ingesamt≥50%
der Pflichtaufgaben) Bearbeiten von ¨Ubungsaufgaben.
•Pr¨ufung: Klausur (ohne Hilfsmittel)
– Typeset by FoilTEX– 13
Literatur
•Skripte:
–aktuelles Semesterhttp:
//www.imn.htwk-leipzig.de/˜waldmann/lehre.html –vorige Semesterhttp://www.imn.htwk-leipzig.de/
˜waldmann/lehre-alt.html
•Entwurfsmuster:http://www.imn.htwk-leipzig.
de/˜waldmann/draft/pub/hal4/emu/
•Maurice Naftalin und Phil Wadler:Java Generics and Collections, O’Reilly 2006
•http://haskell.org/(Sprache, Werkzeuge, Tutorials),http://book.realworldhaskell.org/
– Typeset by Foil – 14
¨Ubungen
•im Pool Z430, vgl.http://www.imn.htwk-leipzig.
de/˜waldmann/etc/pool/
•Beispiele f. deklarative Programmierung –funktional: Haskell mit ghci,
–logisch: Prolog mit swipl, –constraint: mit mathsat, z3
•Haskell-Entwicklungswerkzeuge
–(eclipsefp, leksah, . . . ,http://xkcd.org/378/) –API-Suchmaschinehttp://www.haskell.org/hoogle/
•Commercial Uses of Functional Programming
http://www.syslog.cl.cam.ac.uk/2013/09/22/
liveblogging-cufp-2013/
– Typeset by Foil – 15
Daten
Wiederholung: Terme
•(Pr¨adikatenlogik)Signatur Σist Menge von Funktionssymbolen mit Stelligkeiten ein Termtin SignaturΣist
–Funktionssymbolf∈Σder Stelligkeitk
mit Argumenten(t1, . . . , tk), die selbst Terme sind.
Term(Σ) =Menge der Terme ¨uber SignaturΣ
•(Graphentheorie) ein Term ist ein gerichteter, geordneter, markierter Baum
•(Datenstrukturen)
–Funktionssymbol=Konstruktor, Term=Baum
– Typeset by FoilTEX– 16
Beispiele: Signatur, Terme
•Signatur:Σ1={Z/0, S/1, f /2}
•Elemente vonTerm(Σ1):
Z(), S(S(Z())), f(S(S(Z())), Z())
•Signatur:Σ2={E/0, A/1, B/1}
•Elemente vonTerm(Σ2): . . .
– Typeset by FoilTEX– 17
Algebraische Datentypen
data Foo = Foo { bar :: Int, baz :: String } deriving Show
Bezeichnungen (benannte Notation)
•data Fooist Typname
•Foo { .. }ist Konstruktor
•bar, bazsind Komponenten x :: Foo
x = Foo { bar = 3, baz = "hal" } Bezeichnungen (positionelle Notation) data Foo = Foo Int String y = Foo 3 "bar"
– Typeset by FoilTEX– 18
Datentyp mit mehreren Konstruktoren
Beispiel (selbst definiert)data T = A { foo :: Int }
| B { bar :: String, baz :: Bool } deriving Show
Bespiele (in Prelude vordefiniert) data Bool = False | True data Ordering = LT | EQ | GT
– Typeset by FoilTEX– 19
Mehrsortige Signaturen
•(bisher) einsortige Signatur
Abbildung von Funktionssymbol nach Stelligkeit
•(neu) mehrsortige Signatur
–Menge von SortensymbolenS={S1, . . .} –Abb. von F.-Symbol nach Typ
–Typist Element ausS∗×S
Folge der Argument-Sorten, Resultat-Sorte Bsp.:S={Z, B},Σ ={07→([], Z), p7→([Z, Z], Z), e7→([Z, Z], B), a7→([B, B], B)}.
•Term(Σ): konkrete Beispiele, allgemeine Definition?
– Typeset by FoilTEX– 20
Rekursive Datentypen
data Tree = Leaf {}| Branch { left :: Tree , right :: Tree }
¨Ubung: Objekte dieses Typs erzeugen
(benannte und positionelle Notation der Konstruktoren)
– Typeset by FoilTEX– 21
Daten mit Baum-Struktur
•mathematisches Modell: Term ¨uber Signatur
•programmiersprachliche Bezeichnung:algebraischer Datentyp(die Konstruktoren bilden eine Algebra)
•praktische Anwendungen:
–Formel-B¨aume (in Aussagen- und Pr¨adikatenlogik) –Suchb¨aume (in VL Algorithmen und Datenstrukturen, in
java.util.TreeSet<E>) –DOM (Document Object Model)
https://www.w3.org/DOM/DOMTR
–JSON (Javascript Object Notation) z.B. f¨ur AJAX http://www.ecma-international.org/
publications/standards/Ecma-404.htm
– Typeset by Foil – 22
Bezeichnungen f ¨ur Teilterme
•Position: Folge von nat¨urlichen Zahlen
(bezeichnet einen Pfad von der Wurzel zu einem Knoten) Beispiel: f¨urt=S(f(S(S(Z())), Z()))
ist[0,1]eine Position int.
•Pos(t) =die Menge der Positionen eines Termst Definition: wennt=f(t1, . . . , tk),
dannPos(t) ={[]} ∪ {[i−1]++p|1≤i≤k∧p∈Pos(ti)}. dabei bezeichnen:
•[]die leere Folge,
•[i]die Folge der L¨ange 1 mit Elementi,
•++den Verkettungsoperator f¨ur Folgen
– Typeset by Foil – 23
Operationen mit (Teil)Termen
•t[p] =der Teilterm vontan Positionp Beispiel:S(f(S(S(Z())), Z()))[0,1] =. . .
Definition (durch Induktion ¨uber die L¨ange vonp): . . .
•t[p:=s]: wiet, aber mit Termsan Positionp Beispiel:S(f(S(S(Z())), Z()))[[0,1] :=S(Z)]x=. . . Definition (durch Induktion ¨uber die L¨ange vonp): . . .
– Typeset by FoilTEX– 24
Operationen mit Variablen in Termen
•Term(Σ, V) =Menge der Terme ¨uber SignaturΣmit Variablen ausV
Beispiel:Σ ={Z/0, S/1, f /2}, V ={y}, f(Z(), y)∈Term(Σ, V).
•Substitutionσ: partielle AbbildungV →Term(Σ) Beispiel:σ1={(y, S(Z()))}
•eine Substitution auf einen Term anwenden:tσ:
Intuition: wiet, aber stattvimmerσ(v) Beispiel:f(Z(), y)σ1=f(Z(), S(Z())) Definition durch Induktion ¨ubert
– Typeset by FoilTEX– 25
Termersetzungssysteme
•Daten=Terme (ohne Variablen)
•ProgrammR=Menge von Regeln
Bsp:R={(f(Z(), y), y), (f(S(x), y), S(f(x, y)))}
•Regel=Paar(l, r)von Termen mit Variablen
•Relation→Rist Menge aller Paare(t, t0)mit –es existiert(l, r)∈R
–es existiert Positionpint
–es existiert Substitutionσ: (Var(l)∪Var(r))→Term(Σ) –so daßt[p] =lσundt0=t[p:=rσ].
– Typeset by FoilTEX– 26
Termersetzungssysteme als Programme
• →RbeschreibteinenSchritt der Rechnung vonR,
•transitive und reflexive H¨ulle→∗R
beschreibtFolgevon Schritten.
•Resultateiner Rechnung ist Term inR-Normalform (:=ohne→R-Nachfolger)
dieses Berechnungsmodell ist im allgemeinen
•nichtdeterministischR1={C(x, y)→x, C(x, y)→y} (ein Term kann mehrere→R-Nachfolger haben, ein Term kann mehrere Normalformen erreichen)
•nicht terminierendR2={p(x, y)→p(y, x)} (es gibt eine unendliche Folge von→R-Schritten, es kann Terme ohne Normalform geben)
– Typeset by FoilTEX– 27
Konstruktor-Systeme
F¨ur TRSR ¨uber SignaturΣ: Symbols∈Σheißt•definiert, wenn∃(l, r)∈R:l[] =s(. . .) (das Symbol in der Wurzel ists)
•sonstKonstruktor.
Das TRSRheißtKonstruktor-TRS, falls:
•definierte Symbole kommen linksnur in den Wurzeln vor
¨Ubung: diese Eigenschaft formal spezifizieren
Beispiele:R1={a(b(x))→b(a(x))}¨uberΣ1={a/1, b/1}, R2={f(f(x, y), z)→f(x, f(y, z))¨uberΣ2={f /2}: definierte Symbole? Konstruktoren? Konstruktor-System?
Funktionale Programme sind ¨ahnlich zu Konstruktor-TRS.
– Typeset by FoilTEX– 28
¨Ubung Terme, TRS
•Geben Sie die Signatur des Terms√
a·a+b·ban.
•Geben Sie ein Elementt∈Term({f /1, g/3, c/0})an mit t[1] =c().
mitghci:
•data T = F T | G T T T | C deriving Show erzeugen Sie o.g. Terme (durch Konstruktoraufrufe) DieGr¨oßeeines Termstist definiert durch
|f(t1, . . . , tk)|= 1 +Pk i=1|ti|.
•Bestimmen Sie|√
a·a+b·b|.
•Beweisen Sie∀Σ :∀t∈Term(Σ) :|t|=|Pos(t)|.
– Typeset by FoilTEX– 29
Vervollst¨andigen Sie die Definition derTiefevon Termen:
depth(f()) = 0
k >0 ⇒ depth(f(t1, . . . , tk)) =. . .
•Bestimmen Siedepth(√
a·a+b·b)
•Beweisen Sie∀Σ :∀t∈Term(Σ) : depth(t)<|t|. F¨ur die SignaturΣ ={Z/0, S/1, f /2}:
•f¨ur welche Substitutionσgiltf(x, Z)σ=f(S(Z), Z)?
•f¨ur diesesσ: bestimmen Sief(x, S(x))σ.
Notation f¨ur Termersetzungsregeln: anstatt(l, r)schreibe l→r.
– Typeset by Foil – 30
Abk¨urzung f¨ur Anwendung von 0-stelligen Symbolen:
anstattZ()schreibeZ.
•F¨urR={f(S(x), y)→f(x, S(y)), f(Z, y)→y} bestimme alleR-Normalformen vonf(S(Z), S(Z)).
•f¨urRd=R∪ {d(x)→f(x, x)}
bestimme alleRd-Normalformen vond(d(S(Z))).
•Bestimme die SignaturΣdvonRd.
Bestimme die Menge der Terme ausTerm(Σd), die Rd-Normalformen sind.
•f¨ur die Signatur{A/2, D/0}:
definiere Termet0=D, ti+1=A(ti, D).
– Typeset by Foil – 31
Zeichnet3. Bestimme|ti|.
•f¨urS={A(A(D, x), y)→A(x, A(x, y))}
bestimmeS-Normalform(en), soweit existieren, der Termet2, t3, t4. Zusatz: vontiallgemein.
Abk¨urzung f¨ur mehrfache Anwendung eines einstelligen Symbols:A(A(A(A(x)))) =A4(x)
•f¨ur{A(B(x))→B(A(x))}
¨uber Signatur{A/1, B/1, E/0}: bestimme Normalform vonAk(Bk(E)) f¨urk= 1,2,3,allgemein.
•f¨ur{A(B(x))→B(B(A(x)))}
– Typeset by FoilTEX– 32
¨uber Signatur{A/1, B/1, E/0}: bestimme Normalform vonAk(B(E)) f¨urk= 1,2,3,allgemein.
– Typeset by FoilTEX– 33
Programme
Funktionale Programme
. . . sind spezielle Term-Ersetzungssysteme. Beispiel:Signatur:Seinstellig,Znullstellig,fzweistellig.
Ersetzungssystem{f(Z, y)→y, f(S(x0), y)→S(f(x0, y))}. Starttermf(S(S(Z)), S(Z)).
entsprechendes funktionales Programm:
data N = Z | S N f :: N -> N -> N f x y = case x of
{ Z -> y ; S x’ -> S (f x’ y) }
Aufruf:f (S (S Z)) (S Z)
Auswertung=Folge von Ersetzungsschritten→∗R
Resultat=Normalform (hat keine→R-Nachfolger)
– Typeset by FoilTEX– 34
Pattern Matching
data Tree = Leaf | Branch Tree Tree size :: Tree -> Intsize t = case t of { ... ; Branch l r -> ... }
•Syntax allgemein:
case t of { <Muster> -> <Ausdruck> ; ... }
•<Muster>enth¨alt Konstruktoren und Variablen, entspricht linker Seite einer Term-Ersetzungs-Regel,
<Ausdruck>entspricht rechter Seite
•Def.:tpaßtzum Musterl: es existiertσmitlσ=t
•dynamische Semantik: f¨ur das erste passende Muster wirdrσausgewertet
•statische Semantik: jedes<Muster>hat gleichen Typ wiet, alle<Ausdruck>haben ¨ubereinstimmenden Typ.
– Typeset by FoilTEX– 35
Eigenschaften von Case-Ausdr ¨ucken
eincase-Ausdruck heißt•disjunkt, wenn die Muster nicht ¨uberlappen
(es gibt keinen Term, der zu mehr als 1 Muster paßt)
•vollst¨andig, wenn die Muster den gesamten Datentyp abdecken
(es gibt keinen Term, der zu keinem Muster paßt) Bespiele (f¨urdata N = F N N | S N | Z) -- nicht disjunkt:
case t of { F (S x) y -> .. ; F x (S y) -> .. } -- nicht vollst¨andig:
case t of { F x y -> .. ; Z -> .. }
– Typeset by FoilTEX– 36
data
und
casetypisches Vorgehen beim Verarbeiten algebraischer Daten vom TypT:
•F¨ur jeden Konstruktor des Datentyps data T = C1 ...
| C2 ...
•schreibe einen Zweig in der Fallunterscheidung f x = case x of
C1 ... -> ...
C2 ... -> ...
•Argumente der Konstruktoren sind Variablen⇒ Case-Ausdruck ist disjunkt und vollst¨andig.
– Typeset by FoilTEX– 37
Peano-Zahlen
data N = Z | S Nplus :: N -> N -> N plus x y = case x of
Z -> y
S x’ -> S (plus x’ y) Aufgaben:
•implementiere Multiplikation, Potenz
•beweise die ¨ublichen Eigenschaften (Addition, Multiplikation sind assoziativ, kommutativ, besitzen neutrales Element)
– Typeset by Foil – 38
Pattern Matching in versch. Sprachen
•Scala: case classeshttp://docs.scala-lang.org/
tutorials/tour/case-classes.html
•C# (7):https:
//github.com/dotnet/roslyn/blob/features/
patterns/docs/features/patterns.md
•Javascript?
Nicht verwechseln mitregular expression matchingzur String-Verarbeitung. Es geht um algebraische (d.h.
baum-artige) Daten!
– Typeset by Foil – 39
¨Ubung Pattern Matching, Programme
•F¨ur die Deklarationen
-- data Bool = False | True (aus Prelude) data T = F T | G T T T | C
entscheide/bestimme f¨ur jeden der folgenden Ausdr¨ucke:
–syntaktisch korrekt?
–statisch korrekt?
–Resultat (dynamische Semantik) –disjunkt? vollst¨andig?
1. case False of { True -> C } 2. case False of { C -> True }
– Typeset by FoilTEX– 40
3. case False of { False -> F F }
4. case G (F C) C (F C) of { G x y z -> F z } 5. case F C of { F (F x) -> False }
6. case F C of { F x -> False ; True -> False } 7. case True of { False -> C ; True -> F C } 8. case True of { False -> C ; False -> F C }
9. case C of { G x y z -> False; F x -> False; C -> True }
•Operationen auf Wahrheitswerten:
import qualified Prelude
data Bool = False | True deriving Prelude.Show not :: Bool -> Bool -- Negation
not x = case x of ... -> ...
– Typeset by FoilTEX– 41
... -> ...
Syntax: wenn nachofkein{folgt:
implizite{ ; }durchAbseitsregel (layout rule).
•(&&) :: Bool -> Bool -> Bool x && y = case ... of ...
Syntax: Funktionsname
–beginnt mit Buchstabe: steht vor Argumenten, –beginnt mit Zeichen: zwischen Argumenten (als
Operator)
Operator als Funktion:(&&) False True, Funktion als Operator:True ‘f‘ False.
•Listen von Wahrheitswerten:
– Typeset by FoilTEX– 42
data List = Nil | Cons Bool List deriving Prelude.Show and :: List -> Bool
and l = case l of ...
entsprechendor :: List -> Bool
•(Wdhlg.) welche Signatur beschreibt bin¨are B¨aume (jeder Knoten hat 2 oder 0 Kinder, die B¨aume sind; es gibt keine Schl¨ussel)
•geben Sie die dazu ¨aquivalentedata-Deklaration an:
data T = ...
•implementieren Sie daf¨ur die Funktionen size :: T -> Prelude.Int
– Typeset by FoilTEX– 43
depth :: T -> Prelude.Int benutzePrelude.+(das ist Operator), Prelude.min, Prelude.max
•f¨ur Peano-Zahlendata N = Z | S N implementieren Sieplus, mal, min, max
– Typeset by FoilTEX– 44
Polymorphie
Definition, Motivation
•Beispiel: bin¨are B¨aume mit Schl¨ussel vom Type data Tree e = Leaf
| Branch (Tree e) e (Tree e) Branch Leaf True Leaf :: Tree Bool
Branch Leaf 42 Leaf :: Tree Int
•Definition:
ein polymorpher Datentyp ist einTypkonstruktor (=eine Funktion, die Typen auf einen Typ abbildet)
•unterscheide:Treeist der Typkonstruktor,Branchist ein Datenkonstruktor
– Typeset by FoilTEX– 45
Beispiele f. Typkonstruktoren (I)
•Kreuzprodukt:
data Pair a b = Pair a b
•disjunkte Vereinigung:
data Either a b = Left a | Right b
•data Maybe a = Nothing | Just a
•Haskell-Notation f¨ur Produkte:
(1,True)::(Int,Bool) f¨ur0,2,3, . . . Komponenten
– Typeset by Foil – 46
Beispiele f. Typkonstruktoren (II)
•bin¨are B¨aume
data Bin a = Leaf
| Branch (Bin a) a (Bin a)
•Listen
data List a = Nil
| Cons a (List a)
•B¨aume
data Tree a = Node a (List (Tree a))
– Typeset by Foil – 47
Polymorphe Funktionen
Beispiele:•Spiegeln einer Liste:
reverse :: forall e . List e -> List e
•Verketten von Listen mit gleichem Elementtyp:
append :: forall e . List e -> List e -> List e
Knotenreihenfolge eines Bin¨arbaumes:
preorder :: forall e . Bin e -> List e Def: der Typ einer polymorphen Funktion beginnt mit All-Quantoren f¨ur Typvariablen.
Bsp: Datenkonstruktoren polymorpher Typen.
– Typeset by FoilTEX– 48
Bezeichnungen f. Polymorphie
data List e = Nil | Cons e (List e)•Listist einTypkonstruktor
•List eist einpolymorpherTyp (ein Typ-Ausdruck mitTyp-Variablen)
•List Boolist einmonomorpher Typ
(entsteht durchInstantiierung: Substitution der Typ-Variablen durch Typen)
•polymorphe Funktion:
reverse:: forall e . List e -> List e monomorphe Funktion:xor:: List Bool -> Bool polymorphe Konstante:Nil::forall e. List e
– Typeset by FoilTEX– 49
Operationen auf Listen (I)
data List a = Nil | Cons a (List a)•append xs ys = case xs of Nil ->
Cons x xs’ ->
• ¨Ubung: formuliere und beweise:appendist assoziativ.
•reverse xs = case xs of Nil ->
Cons x xs’ ->
•beweise:
forall xs . reverse (reverse xs) == xs
– Typeset by FoilTEX– 50
Operationen auf Listen (II)
Die vorige Implementierung vonreverseist (f¨ur einfach verkettete Listen) nicht effizient.
Besser ist:
reverse xs = rev_app xs Nil mit Spezifikation
rev_app xs ys = append (reverse xs) ys
¨Ubung: daraus die Implementierung vonrev_appableiten rev_app xs ys = case xs of ...
– Typeset by FoilTEX– 51
Operationen auf B¨aumen
data List e = Nil | Cons e (List e)data Bin e = Leaf | Branch (Bin e) e (Bin e) Knotenreihenfolgen
•preorder :: forall e . Bin e -> List e preorder t = case t of ...
•entsprechendinorder,postorder
•und Rekonstruktionsaufgaben
Adressierug von Knoten (False=links,True=rechts)
•get :: Tree e -> List Bool -> Maybe e
•positions :: Tree e -> List (List Bool)
– Typeset by FoilTEX– 52
¨Ubung Polymorphie
Geben Sie alle Elemente dieser Datentypen an:•Maybe ()
•Maybe (Bool, Maybe ())
•Either (Bool,Bool) (Maybe (Maybe Bool)) Operationen auf Listen:
•append, reverse, rev app Operationen auf B¨aumen:
•preorder, inorder, postorder, (Rekonstruktion)
•get, (positions)
– Typeset by FoilTEX– 53
Kochrezept: Objektkonstruktion
Aufgabe (Bsp):x :: Either (Maybe ()) (Pair Bool ()) L¨osung (Bsp):
•der TypEither a bhat Konstruktoren Left a | Right b. W¨ahleRight b.
Die Substitution f¨ur die Typvariablen ist a = Maybe (), b = Pair Bool ().
x = Right ymity :: Pair Bool ()
•der TypPair a bhat KonstruktorPair a b.
die Substitution f¨ur diese Typvariablen ist a = Bool, b = ().
– Typeset by Foil – 54
y = Pair p qmitp :: Bool, q :: ()
•der TypBoolhat KonstruktorenFalse | True, w¨ahle p = False. der Typ()hat Konstruktor(), alsoq=() Insgesamtx = Right y = Right (Pair False ()) Vorgehen (allgemein)
•bestimme den Typkonstruktor
•bestimme die Substitution f¨ur die Typvariablen
•w¨ahle einen Datenkonstruktor
•bestimme Anzahl und Typ seiner Argumente
•w¨ahle Werte f¨ur diese Argumente nach diesem Vorgehen.
– Typeset by Foil – 55
Kochrezept: Typ-Bestimmung
Aufgabe (Bsp.) bestimme Typ vonx(erstes Arg. vonget):
at :: Position -> Tree a -> Maybe a at p t = case t of
Node f ts -> case p of Nil -> Just f
Cons x p’ -> case get x ts of Nothing -> Nothing
Just t’ -> at p’ t’
L¨osung:
•bestimme das Muster, durch welchesxdeklariert wird.
L¨osung:Cons x p’ ->
•bestimme den Typ diese Musters
– Typeset by FoilTEX– 56
L¨osung: ist gleich dem Typ der zugeh¨origen Diskriminantep
•bestimme das Muster, durch daspdeklariert wird L¨osung:at p t =
•bestimme den Typ vonp
L¨osung: durch Vergleich mit Typdeklaration vonat(pist das erste Argument)p :: Position, also
Cons x p’ :: Position = List N, alsox :: N.
Vorgehen zur Typbestimmung eines Namens:
•finde die Deklaration (Muster einer Fallunterscheidung oder einer Funktionsdefinition)
•bestimme den Typ des Musters (Fallunterscheidung: Typ der Diskriminante, Funktion: deklarierter Typ)
– Typeset by FoilTEX– 57
Statische Typisierung und Polymorphie
Def: dynamische Typisierung:•die Daten (zur Laufzeit des Programms, im Hauptspeicher) haben einen Typ
Def: statische Typisierung:
•Bezeichner, Ausdr¨ucke (im Quelltext) haben einen Type (zur ¨Ubersetzungszeit bestimmt).
•f¨urjedeAusf¨uhrung des Programms gilt: der statische Typ eines Ausdrucks ist gleich dem dynamischen Typ seines Wertes
– Typeset by FoilTEX– 58
Bsp. f¨ur Programm ohne statischen Typ (Javascript) function f (x) {
if (x>0) { return function () { return 42; } } else { return "foobar"; } }
Dann: Auswertung vonf(1)()ergibt 42, Auswertung von f(0)()ergibt Laufzeit-Typfehler.
entsprechendes Haskell-Programm ist statisch fehlerhaft f x = case x > 0 of
True -> \ () -> 42 False -> "foobar"
– Typeset by FoilTEX– 59
Nutzen der statischen Typisierung:
•beim Programmieren: Entwurfsfehler werden zu Typfehlern, diese werden zur Entwurfszeit automatisch erkannt⇒fr¨uher erkannte Fehler lassen sich leichter beheben
•beim Ausf¨uhren: es gibt keine Lauzeit-Typfehler⇒keine Typpr¨ufung zur Laufzeit n¨otig, effiziente Ausf¨uhrung Nutzen der Polymorphie:
•Flexibilit¨at, nachnutzbarer Code, z.B. Anwender einer Collection-Bibliothek legt Element-Typ fest (Entwickler der Bibliothek kennt den Element-Typ nicht)
•gleichzeitig bleibt statische Typsicherheit erhalten
– Typeset by FoilTEX– 60
Von der Spezifikation zur Implementierung (I)
Bsp: Addition von Peano-Zahlendata N = Z | S N plus :: N -> N -> Naus der Typdeklaration wird abgeleitet:
plus x y = case x of Z ->
S x’ ->
erster Zweig:plus Z y = 0 + y = y
zweiter Zweig :plus (S x’) y = (1 + x’) + y= mit Assoziativit¨at von+gilt
... = 1 + (x’ + y) = S (plus x’ y)
Bsp. ( ¨U): Multiplikation. Hinweis: benutze Distributivgesetz.
– Typeset by FoilTEX– 61
Von der Spezifikation zur Implementierung (II)
Bsp: homogene Listendata List a = Nil | Cons a (List a) Aufgabe: implementieremaximum :: List N -> N Spezifikation:
maximum (Cons x1 Nil) = x1
maximum (append xs ys) = max (maximum xs) (maximum ys)
•substitutierexs = Nil, erhalte
maximum (append Nil ys) = maximum ys
= max (maximum Nil) (maximum ys)
d.h.maximum Nilsollte das neutrale Element f¨urmax (auf nat¨urlichen Zahlen) sein, also0(geschriebenZ).
– Typeset by Foil – 62
•substitutierexs = Cons x1 Nil, erhalte maximum (append (Cons x1 Nil) ys)
= maximum (Cons x1 ys)
= max (maximum (Cons x1 Nil)) (maximum ys)
= max x1 (maximum ys)
Damit kann der aus dem Typ abgeleitete Quelltext maximum :: List N -> N
maximum xs = case xs of Nil ->
Cons x xs’ ->
erg¨anzt werden.
Vorsicht: f¨urmin, minimumfunktioniert das nicht so, denn minhat f¨urNkein neutrales Element.
– Typeset by Foil – 63
Unver¨anderliche Objekte
¨Uberblick
•alle Attribute aller Objekte sind unver¨anderlich (final)
•anstatt Objekt zu ¨andern, konstruiert man ein neues Eigenschaften des Programmierstils:
•vereinfacht Formulierung und Beweis von Objekteigenschaften
•parallelisierbar (keine updates, keinedata races) http://fpcomplete.com/
the-downfall-of-imperative-programming/
•Persistenz (Verf¨ugbarkeit fr¨uherer Versionen)
•Belastung des Garbage Collectors (. . . daf¨ur ist er da)
– Typeset by FoilTEX– 64
Beispiel: Einf ¨ugen in Baum
•destruktiv:
interface Tree<K> { void insert (K key); } Tree<String> t = ... ;
t.insert ("foo");
•persistent (Java):
interface Tree<K> { Tree<K> insert (K key); } Tree<String> t = ... ;
Tree<String> u = t.insert ("foo");
•persistent (Haskell):
insert :: Tree k -> k -> Tree k
– Typeset by FoilTEX– 65
Beispiel: (unbalancierter) Suchbaum
data Tree k = Leaf| Branch (Tree k) k (Tree k) insert :: Ord k => k -> Tree k -> Tree k insert k t = case t of ...
Diskussion:
•Ord kentsprichtK implements Comparable<K>, genaueres sp¨ater (Haskell-Typklassen)
•wie teuer ist die Persistenz?
(wieviel M¨ull entsteht bei eineminsert?)
– Typeset by FoilTEX– 66
Beispiel: Sortieren mit Suchb¨aumen
data Tree k = Leaf| Branch (Tree k) k (Tree k) insert :: Ord k => k -> Tree k -> Tree k build :: Ord k => [k] -> Tree k
build = foldr ... ...
sort :: Ord k => [k] -> [k]
sort xs = ... ( ... xs )
– Typeset by FoilTEX– 67
Persistente Objekte in Git
http://git-scm.com/•Distributed development.
•Strong support fornon-lineardevelopment.
(Branching and merging are fast and easy.)
•Efficient handling oflargeprojects.
(z. B. Linux-Kernel,http://kernel.org/)
•Toolkit design.
•Cryptographic authentication of history.
– Typeset by FoilTEX– 68
Objekt-Versionierung in Git
•Objekt-Typen:
–Datei (blob),
–Verzeichnis (tree), mit Verweisen auf blobs und trees –Commit, mit Verweisen auf tree u. commits (Vorg¨anger)
git cat-file -p <hash>
•Objekte sindunver¨anderlichund durch SHA1-Hash (160 bit=40 Hex-Zeichen) identifiziert
•statt ¨Uberschreiben: neue Objekte anlegen
•jeder Zustand ist durch Commit-Hash (weltweit) eindeutig beschrieben und kann wiederhergestellt werden
Quelltexte zur Vorlesung:https://gitlab.imn.
htwk-leipzig.de/waldmann/fop-ss16
– Typeset by FoilTEX– 69
Funktionen
Funktionen als Daten
bisher:f :: Int -> Int f x = 2 * x + 5
¨aquivalent: Lambda-Ausdruck f = \ x -> 2 * x + 5
Lambda-Kalk¨ul: Alonzo Church 1936, Henk Barendregt 198*, . . .
Funktionsanwendung:
(\ x -> B) A = B [x := A]
ist nur erlaubt, falls keine inAfreie Variable durch ein Lambda inBgebunden wird.
– Typeset by Foil – 70
Der Lambda-Kalk ¨ul
. . . als weiteres Berechnungsmodell,(vgl. Termersetzungssysteme, Turingmaschine, Random-Access-Maschine)
Syntax: die Menge der Lambda-TermeΛist
•jede Variable ist ein Term:v∈V ⇒v∈Λ
•Funktionsanwendung (Applikation):
F ∈Λ, A∈Λ⇒(F A)∈Λ
•Funktionsdefinition (Abstraktion):
v∈V, B∈Λ⇒(λv.B)∈Λ Semantik: eine Relation→βaufΛ (vgl.→Rf¨ur TermersetzungssystemR)
– Typeset by Foil – 71
Freie und gebundene Variablen(vorkommen)
•Das Vorkommen vonv∈V an Positionpin Termtheißt frei, wenn ”dar¨uber keinλv. . . . steht“
•Def.fvar(t) =Menge der intfrei vorkommenden Variablen (definiere durch strukturelle Induktion)
•Eine Variablexheißt inAgebunden, fallsAeinen Teilausdruckλx.Benth¨alt.
•Def.bvar(t) =Menge der intgebundenen Variablen Bsp:fvar(x(λx.λy.x)) ={x},bvar(x(λx.λy.x)) ={x, y},
– Typeset by FoilTEX– 72
Semantik des Lambda-Kalk ¨uls: Reduktion
→βRelation→βaufΛ(ein Reduktionsschritt) Es giltt→βt0, falls
• ∃p∈Pos(t), so daß
•t[p] = (λx.B)Amitbvar(B)∩fvar(A) =∅
•t0=t[p:=B[x:=A]]
dabei bezeichnetB[x:=A]ein Kopie vonB, bei der jedes freie Vorkommen vonxdurchAersetzt ist Ein (Teil-)Ausdruck der Form(λx.B)AheißtRedex.
(Dort kann weitergerechnet werden.) Ein Term ohne Redex heißtNormalform.
(Normalformen sind Resultate von Rechnungen.)
– Typeset by FoilTEX– 73
Semantik . . . : gebundene Umbenennung
→α•Relation→αaufΛ, beschreibtgebundene Umbenennung einer lokalen Variablen.
•Beispielλx.f xz→αλy.f yz.
(fundzsind frei, k¨onnen nicht umbenannt werden)
•Definitiont→αt0:
–∃p∈Pos(t), so daßt[p] = (λx.B) –y /∈bvar(B)∪fvar(B)
–t0=t[p:=λy.B[x:=y]]
•wird angewendet, umbvar(B)∩fvar(A) =∅in Regel f¨ur
→βzu erf¨ullen.
Bsp: betrachte den unterstrichenen Redex in (λx.((λf.(λx.(x+f8)))(λy.(x+y))))3
– Typeset by FoilTEX– 74
Umbenennung von lokalen Variablen
int x = 3;
int f(int y) { return x + y; } int g(int x) { return (x + f(8)); } // g(5) => 16
Darff(8)ersetzt werden durchf[y:= 8]? - Nein:
int x = 3;
int g(int x) { return (x + (x+8)); } // g(5) => 18
Das freiexin(x+y)wird f¨alschlich gebunden.
L¨osung: lokal umbenennen
int g(int z) { return (z + f(8)); }
dann ist Ersetzung erlaubt
int x = 3;
int g(int z) { return (z + (x+8)); } // g(5) => 16
– Typeset by FoilTEX– 75
Lambda-Terme: verk ¨urzte Notation
•Applikation ist links-assoziativ, Klammern weglassen:
(. . .((F A1)A2). . . An)∼F A1A2. . . An
Beispiel:((xz)(yz))∼xz(yz) Wirkt auch hinter dem Punkt:
(λx.xx)bedeutet(λx.(xx))— und nicht((λx.x)x)
•geschachtelte Abstraktionen unter ein Lambda schreiben:
(λx1.(λx2. . . .(λxn.B). . .))∼λx1x2. . . xn.B Beispiel:λx.λy.λz.B∼λxyz.B
– Typeset by FoilTEX– 76
Ein- und mehrstellige Funktionen
eine einstellige Funktion zweiter Ordnung:f = \ x -> ( \ y -> ( x*x + y*y ) ) Anwendung dieser Funktion:
(f 3) 4 = ...
Kurzschreibweisen (Klammern weglassen):
f = \ x y -> x * x + y * y ; f 3 4
¨Ubung:
gegebent = \ f x -> f (f x) bestimmet succ 0, t t succ 0, t t t succ 0, t t t t succ 0, ...
– Typeset by FoilTEX– 77
Typen
f¨ur nicht polymorphe Typen: tats¨achlicher Argumenttyp muß mit deklariertem Argumenttyp ¨ubereinstimmen:
wennf::A→Bundx::A, dann(f x) ::B.
bei polymorphen Typen k¨onnen der Typ vonf ::A→B und der Typ vonx::A0Typvariablen enthalten.
Beispiel:λx.x::∀t.t→t.
Dann m¨ussenAundA0nicht ¨ubereinstimmen, sondern nur unifizierbar sein (eine gemeinsame Instanz besitzen).
Beispiel:(λx.x)True
benutze Typ-Substitutionσ={(t,Bool)}.
Bestimme allgemeinsten Typ vont=λf x.f(f x)), von(tt).
– Typeset by Foil – 78
Beispiel f ¨ur Typ-Bestimmung
Aufgabe: bestimme den allgemeinsten Typ vonλf x.f(f x)
•Ansatz mit Typvariablenf::t1, x::t2
•betrachte(f x): der Typ vonfmuß ein Funktionstyp sein, alsot1= (t11→t12)mit neuen Variablent11, t12.
Dann giltt11=t2und(f x) ::t12.
•betrachtef(f x). Wir habenf::t11→t12und(f x) ::t12, also folgtt11=t12. Dannf(f x) ::t12.
•betrachteλx.f(f x).
Ausx::t12undf(f x) ::t12folgtλx.f(f x) ::t12→t12.
•betrachteλf.(λx.f(f x)).
Ausf ::t12→t12undλx.f(f x) ::t12→t12
folgtλf x.f(f x) :: (t12→t12)→(t12→t12)
– Typeset by Foil – 79
Verk ¨urzte Notation f ¨ur Typen
•Der Typ-Pfeil istrechts-assoziativ:
T1→T2→ · · · →Tn→T bedeutet (T1→(T2→ · · · →(Tn→T)· · ·))
•das paßt zu den Abk¨urzungen f¨ur mehrstellige Funktionen:
λ(x::T1).λ(x::T2).(B::T) hat den Typ(T1→(T2→B)), mit o.g. Abk¨urzungT1→T2→T.
– Typeset by FoilTEX– 80
Lambda-Ausdr ¨ucke in C#
•Beispiel (Fkt. 1. Ordnung)
Func<int,int> f = (int x) => x*x;
f (7);
• ¨Ubung (Fkt. 2. Ordnung) — erg¨anze alle Typen:
??? t = (??? g) => (??? x) => g (g (x));
t (f)(3);
•Anwendungen bei Streams, sp¨ater mehr
(new int[]{3,1,4,1,5,9}).Select(x => x * 2);
(new int[]{3,1,4,1,5,9}).Where(x => x > 3);
• ¨Ubung: Diskutiere statische/dynamische Semantik von
(new int[]{3,1,4,1,5,9}).Select(x => x > 3);
(new int[]{3,1,4,1,5,9}).Where(x => x * 2);
– Typeset by FoilTEX– 81
Lambda-Ausdr ¨ucke in Java(8)
funktionalesInterface (FI): hat genau eine Methode Lambda-Ausdruck (”burger arrow“) erzeugt Objekt einer anonymen Klasse, die FI implementiert.interface I { int foo (int x); } I f = (x)-> x+1;
System.out.println (f.foo(8));
vordefinierte FIs:
import java.util.function.*;
Function<Integer,Integer> g = (x)-> x*2;
System.out.println (g.apply(8));
Predicate<Integer> p = (x)-> x > 3;
if (p.test(4)) { System.out.println ("foo"); }
– Typeset by FoilTEX– 82
Lambda-Ausdr ¨ucke in Javascript
$ node
> var f = function (x){return x+3;}
undefined
> f(4) 7
– Typeset by FoilTEX– 83
Beispiele Fkt. h ¨oherer Ord.
•Haskell-Notation f¨ur Listen:
data List a = Nil | Cons a (List a) data [a] = [] | a : [a]
•Verarbeitung von Listen:
filter :: (a -> Bool) -> [a] -> [a]
takeWhile :: (a -> Bool) -> [a] -> [a]
partition :: (a -> Bool) -> [a] -> ([a],[a])
•Vergleichen, Ordnen:
nubBy :: (a -> a -> Bool) -> [a] -> [a]
data Ordering = LT | EQ | GT minimumBy
:: (a -> a -> Ordering) -> [a] -> a
– Typeset by FoilTEX– 84
¨Ubung Lambda-Kalk¨ul
•Wiederholung: konkrete Syntax, abstrakte Syntax, Semantik
•S=λxyz.xz(yz), K =λab.a, Normalform vonSKKc
•(mitdata N=Z|S N) bestimme Normalform vonttSZ f¨urt=λf x.f(f x),
•definiereΛals algebraischen Datentypdata L = ...
(3 Konstruktoren)
implementieresize :: L -> Int, depth :: L -> Int.
implementierebvar :: L -> S.Set String,
– Typeset by FoilTEX– 85
fvar :: L -> S.Set String,
siehe Folie mit Definitionen und dort angegebene Testf¨alle
benutzeimport qualified Data.Set as S, API-Dokumentation:https://hackage.haskell.
org/package/containers/docs/Data-Set.html
•autotool-Aufgaben Lambda-Kalk¨ul
– Typeset by Foil – 86
¨Ubung Fkt. h¨oherer Ordnung
•Typisierung, Beispiele in Haskell, C#, Java, Javascript compose ::
compose = \ f g -> \ x -> f (g x)
•Implementierung von takeWhile, dropWhile
– Typeset by Foil – 87
Rekursionsmuster
Rekursion ¨uber B¨aume (Beispiele)
data Tree a = Leaf| Branch (Tree a) a (Tree a) summe :: Tree Int -> Int
summe t = case t of Leaf -> 0
Branch l k r -> summe l + k + summe r preorder :: Tree a -> List a
preorder t = case t of Leaf -> Nil
Branch l k r ->
Cons k (append (preorder l) (preorder r))
– Typeset by FoilTEX– 88
Rekursion ¨uber B¨aume (Schema)
f :: Tree a -> bf t = case t of Leaf -> ...
Branch l k r -> ... (f l) k (f r) dieses Schemaisteine Funktion h¨oherer Ordnung:
fold :: ( ... ) -> ( ... ) -> ( Tree a -> b ) fold leaf branch = \ t -> case t of
Leaf -> leaf Branch l k r ->
branch (fold leaf branch l) k (fold leaf branch r)
summe = fold 0 ( \ l k r -> l + k + r )
– Typeset by FoilTEX– 89
Rekursion ¨uber Listen
and :: List Bool -> Booland xs = case xs of
Nil -> True ; Cons x xs’ -> x && and xs’
length :: List a -> N length xs = case xs of
Nil -> Z ; Cons x xs’ -> S (length xs’) fold :: b -> ( a -> b -> b ) -> List a -> b fold nil cons xs = case xs of
Nil -> nil
Cons x xs’ -> cons x ( fold nil cons xs’ ) and = fold True (&&)
length = fold Z ( \ x y -> S y)
– Typeset by FoilTEX– 90
Rekursionsmuster (Prinzip)
ein Rekursionsmuster anwenden=jeden Konstruktor durch eine passende Funktion ersetzen.data List a = Nil | Cons a (List a) fold ( nil :: b ) ( cons :: a -> b -> b )
:: List a -> b
Rekursionsmuster instantiieren=(Konstruktor-)Symbole interpretieren (durch Funktionen)=eine Algebra angeben.
length = fold Z ( \ _ l -> S l ) reverse = fold Nil ( \ x ys -> )
– Typeset by FoilTEX– 91
Rekursionsmuster (Merks¨atze)
aus dem Prinzipein Rekursionsmuster anwenden=jeden Konstruktor durch eine passende Funktion ersetzenfolgt:
•Anzahl der Muster-Argumente=Anzahl der Konstruktoren (plus eins f¨ur das Datenargument)
•Stelligkeit eines Muster-Argumentes=Stelligkeit des entsprechenden Konstruktors
•Rekursion im Typ⇒Rekursion im Muster (Bsp: zweites Argument vonCons)
•zu jedem rekursiven Datentyp gibt esgenau ein passendes Rekursionsmuster
– Typeset by FoilTEX– 92
Rekursion ¨uber Listen ( ¨Ubung)
das vordefinierte Rekursionsschema ¨uber Listen ist:foldr :: (a -> b -> b) -> b -> ([a] -> b) length = foldr ( \ x y -> 1 + y ) 0 Beachte:
•Argument-Reihenfolge (erst cons, dann nil)
•foldr nicht mit foldl verwechseln (foldr ist das ”richtige“) Aufgaben:
•append, reverse, concat, inits, tails mitfoldr(d. h., ohne Rekursion)
– Typeset by FoilTEX– 93
Weitere Beispiele f ¨ur Folds
data Tree a = Leaf a | Branch (Tree a) (Tree a) fold :: ...
•Anzahl der Bl¨atter
•Anzahl der Verzweigungsknoten
•Summe der Schl¨ussel
•die Tiefe des Baumes
•der gr¨oßte Schl¨ussel
– Typeset by Foil – 94
Rekursionsmuster (Peano-Zahlen)
data N = Z | S Nfold :: ...
fold z s n = case n of Z ->
S n’ ->
plus = fold ...
times = fold ...
– Typeset by Foil – 95
¨Ubung Rekursionsmuster
•Rekursionsmusterfoldrf¨ur Listen benutzen (filter, takeWhile, append, reverse, concat, inits, tails)
•Rekursionmuster f¨ur Peano-Zahlen hinschreiben und benutzen (plus, mal, hoch, Nachfolger, Vorg¨anger, minus)
•Rekursionmuster f¨ur bin¨are B¨aume mit Schl¨usselnnur in den Bl¨atternhinschreiben und benutzen
•Rekursionmuster f¨ur bin¨are B¨aume mit Schl¨usselnnur in den Verzweigungsknotenbenutzen f¨ur rekursionslose Programme f¨ur:
–Anzahl der Branch-Knoten ist ungerade (nicht z¨ahlen!) –Baum (Tree a) erf¨ullt die AVL-Bedingung
– Typeset by FoilTEX– 96
Hinweis: als Projektion auf die erste Komponente eines fold, das Paar vonBool(ist AVL-Baum) undInt (H¨ohe) berechnet.
–Baum (Tree Int) ist Suchbaum (ohneinorder) Hinweis: als Projektion. Bestimme geeignete Hilfsdaten.
•Wende die Vorschrift zur Konstruktion des Rekursionsmusters an auf den Typ –Bool
–Maybe a Jeweils:
–Typ und Implementierung –Testf¨alle
–gibt es diese Funktion bereits? Suche nach dem Typ mit
– Typeset by FoilTEX– 97
https://www.stackage.org/lts-5.17/hoogle
– Typeset by FoilTEX– 98
Objektorientierte Entwurfmuster
Definition, Geschichte
•Ziel: flexibel wiederverwendbarer sicherer Quelltext
•L¨osung:Funktionen h¨oherer Ordnung
•Simulation davon im OO-Paragidma:Entwurfsmuster wir wollen: Funktion als Datum (z.B. Lambda-Ausdruck), wir konstruieren: Objekt, das zu einer (anonymen) Klasse geh¨ort, die diese Funktion als Methode enth¨alt.
•Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides:Entwurfsmuster (design patterns)— Elemente wiederverwendbarer objektorientierter Software,
Addison-Wesley 1996.
– Typeset by FoilTEX– 99
Beispiel Strategie-Muster
•Aufgabe: Sortieren einer Liste bzgl. w¨ahlbarer Ordnung auf Elementen.
•L¨osung (inData.List)
data Ordering = LT | EQ | GT
sortBy :: (a -> a -> Ordering) -> List a -> List a
( ¨U: implementiere durch unbalancierten Suchbaum)
•Simulation (injava.util.*)
interface Comparator<T> { int compare(T x, T y); } static <T> void sort(List<T> list,Comparator<T> c);
hier istceinStrategie-Objekt
– Typeset by FoilTEX– 100
Algebraische Datentypen in OOP
Kompositum: Motivation
•Bsp: Gestaltung von zusammengesetzten Layouts.
Modell als algebraischer Datentyp:
data Component = JButton { ... }
| Container (List Component)
•Simulation durch EntwurfsmusterKompositum:
–abstract class Component
–class JButton extends Component –class Container extends Component – { void add (Component c); }
– Typeset by FoilTEX– 101
Kompositum: Beispiel
public class Composite {
public static void main(String[] args) { JFrame f = new JFrame ("Composite");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = new JPanel (new BorderLayout());
c.add (new JButton ("foo"), BorderLayout.CENTER);
f.getContentPane().add(c);
f.pack(); f.setVisible(true);
} }
¨Ubung: geschachtelte Layouts bauen, vgl.
http://www.imn.htwk-leipzig.de/˜waldmann/
edu/ws06/informatik/manage/
– Typeset by Foil – 102
Kompositum: Definition
•Definition:Kompositum=algebraischer Datentyp (ADT)
•ADTdata T = .. | C .. T ..
als Kompositum:
–TypT⇒gemeinsame Basisklasse (interface) –jeder KonstruktorC⇒implementierende Klasse –jedes Argument des Konstruktors⇒Attribut der Klasse –diese Argumente k¨onnenTbenutzen (rekursiver Typ) (Vorsicht: Begriff und Abk¨urzung nicht verwechseln mit abstrakter Datentyp=ein Typ, dessen Datenkonstruktoren wirnichtsehen)
– Typeset by Foil – 103
Bin¨are B¨aume als Komposita
•Knoten sindinnere(Verzweigung) und¨außere(Blatt).
•Die richtige Realisierung ist Kompositum interface Tree<K>;
class Branch<K> implements Tree<K>;
class Leaf<K> implements Tree<K>;
•Schl¨ussel: in allen Knoten, nur innen, nur außen.
der entsprechende algebraische Datentyp ist:
data Tree k = Leaf { ... }
| Branch { left :: Tree k , ...
, right :: Tree k }
¨Ubung: Anzahl aller Bl¨atter, Summe aller Schl¨ussel (Typ?), der gr¨oßte Schl¨ussel (Typ?)
– Typeset by FoilTEX– 104
Kompositum-Vermeidung
Wenn Bl¨atter keine Schl¨ussel haben, geht es musterfrei?
class Tree<K> {
Tree<K> left; K key; Tree<K> right;
}
Der entsprechende algebraische Datentyp ist data Tree k =
Tree { left :: Maybe (Tree k) , key :: k
, right :: Maybe (Tree k) }
erzeugt in Java das Problem, daß . . .
¨Ubung: betrachte Implementierung in java.util.Map<K,V>
– Typeset by FoilTEX– 105
Maybe
=Nullable
Algebraischer Datentyp (Haskell):data Maybe a = Nothing | Just a http:
//hackage.haskell.org/packages/archive/
base/latest/doc/html/Prelude.html#t:Maybe In Sprachen mit Verweisen (auf Objekte vom TypO) gibt es h¨aufig auch ”Verweis auf kein Objekt“— auch vom TypO.
Deswegennull pointer exceptions.
Ursache ist Verwechslung vonMaybe amita.
Trennung in C#:Nullable<T>(f¨ur primitive TypenT) http://msdn.microsoft.com/en-us/library/
2cf62fcy.aspx
– Typeset by FoilTEX– 106
Alg. DT und Pattern Matching in Scala
http://scala-lang.orgalgebraische Datentypen:
abstract class Tree[A]
case class Leaf[A](key: A) extends Tree[A]
case class Branch[A]
(left: Tree[A], right: Tree[A]) extends Tree[A]
pattern matching:
def size[A](t: Tree[A]): Int = t match { case Leaf(k) => 1
case Branch(l, r) => size(l) + size(r) }
beachte: Typparameter in eckigen Klammern
– Typeset by FoilTEX– 107
Objektorientierte Rekursionsmuster
Plan
•algebraischer Datentyp=Kompositum (Typ⇒Interface, Konstruktor⇒Klasse)
•Rekursionsschema=Besucher (Visitor) (Realisierung der Fallunterscheidung)
(Zum Vergleich von Java- und Haskell-Programmierung) sagte bereits Albert Einstein:Das Holzhacken ist deswegen so beliebt, weil man den Erfolg sofort sieht.
– Typeset by FoilTEX– 108
Wiederholung Rekursionsschema
foldanwenden: jeden Konstruktor d. Funktion ersetzen•Konstruktor⇒Schema-Argument
•. . . mit gleicher Stelligkeit
•Rekursion im Typ⇒Anwendung auf Schema-Resultat
data Tree a = Leaf a | Branch (Tree a) (Tree a) Leaf :: a -> Tree a
Branch :: Tree a -> Tree a -> Tree a
fold :: (a -> b) -> (b -> b -> b) -> Tree a -> b fold leaf branch t = case t of
Leaf k -> leaf k
Branch l r -> branch (fold leaf branch l) (fold leaf branch r) depth :: Tree a -> Int
depth = fold ( \ k -> 0 ) ( \ x y -> 1 + max x y )
– Typeset by FoilTEX– 109
Wiederholung: Kompositum
Haskell: algebraischer Datentypdata Tree a = Leaf a
| Branch (Tree a) (Tree a) Leaf :: a -> Tree a
Branch :: Tree a -> Tree a -> Tree a Java: Kompositum
interface Tree<A> { }
class Leaf<A> implements Tree<A> { A key; } class Branch<A> implements Tree<A> {
Tree<A> left; Tree<A> right;
}
(Scala: case class)
– Typeset by Foil – 110
¨Ubung Kompositum
public class Main {
// vollst. Bin¨arbaum der Tiefe d
// mit Schl¨usseln 2ˆd * (c - 1) .. 2ˆd * c - 1 static Tree<Integer> build (int d, int c);
class Pair<A,B> { A first; B second; }
// (Schl¨ussel links außen, Schl. rechts außen) static <A> Pair<A,A> bounds (Tree<A> t);
public static void main(String[] args) { Tree<Integer> t = Main.build(4,1);
System.out.println (Main.bounds(t));
} }
– Typeset by Foil – 111
Kompositum und Visitor
Definition eines Besucher-Objektes(f¨ur Rekursionsmuster mit ResultattypR ¨uberTree<A>) entspricht einem Tupel von Funktionen
interface Visitor<A,R> { R leaf(A k);
R branch(R x, R y); } Empfangen eines Besuchers:
durch jeden Teilnehmer des Kompositums interface Tree<A> { ..
<R> R receive (Visitor<A,R> v); }
•Implementierung
•Anwendung (Bl¨atter z¨ahlen, Tiefe, Spiegelbild)
– Typeset by FoilTEX– 112
Aufgabe: Besucher f ¨ur Listen
Schreibe das Kompositum f¨urdata List a = Nil | Cons a (List a) und den passenden Besucher. Benutze f¨ur
•Summe, Produkt f¨urList<Integer>
•Und, Oder f¨urList<Boolean>
•Wert als gespiegelte Bin¨arzahl (LSB ist links) Bsp:[1,1,0,1] ==> 11
Quelltexte aus Vorlesung:
https://gitlab.imn.htwk-leipzig.de/
waldmann/fop-ss16
– Typeset by FoilTEX– 113
Eine Funktion, die kein Fold ist
Das geht:f xs = die L¨ange von xs ist gerade f = fold True ( \ x y -> not y )Das geht nicht:g xs = die L¨ange von xs ist >= 2 Beweis: falls dochg = fold nil cons, dann betrachte l0 = Nil ; g l0 = False -- nach Spez.
l1 = Cons 4 Nil ; g l1 = False -- nach Spez.
g (Cons 2 l0) = False -- nach Spezifikation g (Cons 2 l0) = cons 2 (g l0) = cons 2 False g (Cons 2 l1) = True -- nach Spezifikation g (Cons 2 l1) = cons 2 (g l1) = cons 2 False es folgt WiderspruchFalse = cons 2 False = True d.h. die Annahme (g = fold nil cons) ist falsch.
– Typeset by FoilTEX– 114
Polymorphie
Arten der Polymorphie
•generische Polymorphie: erkennbar an Typvariablen zur ¨Ubersetzungszeit werden Typvariablen durch konkrete Typen substituiert,
•dynamische Polymorphie (≈Objektorientierung) erkennbar animplementszw. Klasse und Schnittstelle zurLaufzeitwird Methodenimplementierung ausgew¨ahlt moderne OO-Sprachen (u.a. Java, C#) bietenbeide Formen der Polymorphie
mitstatischer Sicherheit (d.h. statische Garantie, daß zur Laufzeit keine Methoden fehlen)
– Typeset by FoilTEX– 115
Java-Notation f. generische Polymorphie
generischerTyp(Typkonstruktor):•Deklaration der Typparameter:class C<S,T> {..}
•bei Benutzung Angabe der Typargumente (Pflicht):
{ C<Boolean,Integer> x = ... } statische generischeMethode:
•Deklaration:class C { static <T> int f(T x) }
•Benutzung:C.<Integer>f (3)
Typargumente k¨onnen auch inferiert werden.
( ¨Ubung: Angabe der Typargumente f¨ur polymorphe nicht statische Methode)
– Typeset by FoilTEX– 116
Beispiel f. dynamische Polymorphie
interface I { int m (); }class A implements I
{ int m () { return 0; }}
class B implements I
{ int m () { return 1; }}
I x = // statischer Typ von x ist I new A(); // dynamischer Typ ist hier A System.out.println (x.m());
x = new B(); // dynamischer Typ ist jetzt B System.out.println (x.m());
•statischer Typ: eines Bezeichners im Programmtext
•dynamischer Typ: einer Stelle im Speicher
– Typeset by FoilTEX– 117
Klassen, Schnittstellen und Entwurfsmuster
•FP-Sichtweise: Entwurfsmuster=Fkt. h¨oherer Ordnung
•OO-Sichtweise: E.M.=n¨utzliche Beziehung zw. Klassen . . . die durch Schnittstellen ausgedr¨uckt wird.
⇒Verwendung von konkreten Typen (Klassen)ist ein Code Smell, es sollen soweit m¨oglich abstrakte Typen (Schnittstellen) sein. ( ¨U: diskutiereIEnumerable)
•insbesondere: in Java (ab 8):
funkionales Interface=hat genau eine Methode eine implementierende anonyme Klasse kann als Lambda-Ausdruck geschrieben werden
– Typeset by Foil – 118
Erzwingen von Abstraktionen
•interface I { .. }
class C implements I { .. } ; Wie kannC x = new C()verhindert werden, undI x = new C()erzwungen?
•Ansatz:class C { private C() { } } aber dann ist auchI x = new C()verboten.
•L¨osung: Fabrik-Methode class C { ..
static I make () { return new C (); } }
– Typeset by Foil – 119
Das Fabrik-Muster
interface I { }class A implements I { A (int x) { .. } } class B implements I { B (int x) { .. } } die Gemeinsamkeit der Konstruktoren kann nicht inI ausgedr¨uckt werden.
interface F // abstrakte Fabrik { I construct (int x); }
class FA implements F // konkrete Fabrik { I construct (int x) { return new A(x); } } class FB implements F { .. }
main () {
F f = Eingabe ? new FA() : new FB();
I o1=f.construct(3); I o2=f.construct(4);
– Typeset by FoilTEX– 120
Typklassen in Haskell: ¨Uberblick
•in einfachen Anwendungsf¨allen:
Typklasse in Haskell∼Schnittstelle in OO:
beschreibt Gemeinsamkeit von konkreten Typen
•–Bsp. der Typ hat eine totale Ordnung Haskell:class Ord a, Java:
interface Comparable<E>
–Bsp. der Typ besitzt Abbildung nachString Haskellclass Show a, Java?
•unterschiedliche Benutzung und Implementierung Haskell - statisch, OO - dynamisch
– Typeset by FoilTEX– 121
Beispiel
sortBy :: (a -> a -> Ordering) -> [a] -> [a]
sortBy ( \ x y -> ... ) [False, True, False]
Kann mit Typklassen so formuliert werden:
class Ord a where
compare :: a -> a -> Ordering sort :: Ord a => [a] -> [a]
instance Ord Bool where compare x y = ...
sort [False, True, False]
•sorthateingeschr¨ankt polymorphen Typ
•die Einschr¨ankung (das ConstraintOrd a) wird in ein zus¨atzliches Argument (eine Funktion) ¨ubersetzt.
Entspricht OO-Methodentabelle, liegt aberstatischfest.
– Typeset by FoilTEX– 122
Unterschiede Typklasse/Interface (Bsp)
•Typklasse/Schnittstelle
class Show a where show :: a -> String interface Show { String show (); }
•Instanzen/Implementierungen
data A = .. ; instance Show A where ..
class A implements Show { .. }entspr. f¨urB
•in Java istShowein Typ:
static String showList(List<Show> xs) { .. } showList (Arrays.asList (new A(),new B())) in Haskell istShowein Typconstraint und kein Typ:
showList :: Show a => List a -> String showList [A,B]ist Typfehler
– Typeset by FoilTEX– 123
Typklassen k ¨onnen mehr als Interfaces
in Java, C#, . . . kann Schnittstelle (interface) inDeklarationen wie Typ (class) benutzt werden, das ist 1. praktisch, aber nur 2. soweit es eben geht
•(?) Fkt. mit>1Argument, Bsp. compareTo,
static <T extends Comparable<? super T>>
void sort(List<T> list)
•(–) Beziehungen zwischen mehreren Typen, class Autotool problem solution
•(–) Typkonstruktorklassen,
class Foldable c where toList :: c a -> [a];
data Tree a = ..;instance Foldable Tree (wichtig f¨ur fortgeschrittene Haskell-Programmierung)
– Typeset by FoilTEX– 124
Grundwissen Typklassen
•Typklasse schr¨ankt statische Polymorphie ein (Typvariable darf nicht beliebig substitutiert werden)
•Einschr¨ankung realisiert durchW¨orterbuch-Argument (W.B.=Methodentabelle, Record von Funktionen)
•durch Instanz-Deklaration wird W¨orterbuch erzeugt
•bei Benutzung einer eingeschr¨ankt polymorphen Funktion: passendes W¨orterbuch wird statisch bestimmt
•n¨utzliche, h¨aufige Typklassen:Show,Read,Eq,Ord.
(Test.SmallCheck.Serial,Foldable,Monad,. . . )
•Instanzen automatisch erzeugen mitderiving
– Typeset by FoilTEX– 125
¨Ubung Polymorphie
•Besucher f¨ur Listen-Kompositum hinzuf¨ugen (Quelltext aus VL), damit implementieren:
class Main {
static Boolean and (List<Boolean> xs) { return xs.receive(new ... );
}
public static void main (String [] argv) { List<Boolean> xs = new Cons<Boolean>(...);
syso (Main.and(xs);
} }
– Typeset by Foil – 126
•nach Java ¨ubersetzen und implementieren
data Pair a b = Pair { first :: a, second :: b } swap :: Pair a b -> Pair b a
p :: Pair Int Bool ; p = Pair 3 False ; q = swap p
•autotool: Aufgabe zu Typklassen (unbalancierte Suchb¨aume)
– Typeset by Foil – 127