Programmieren in Haskell

Werbung
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Programmieren in Haskell
WS 2013/2014
LambdaExpressions
Gestaffelte
Funktionen
Higher Order
Functions
Robert Giegerich
Universität Bielefeld
AG Praktische Informatik
11. Dezember 2013
Funktionen höherer Ordnung
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Worum geht es heute?
LambdaExpressions
In Haskell gibt es Funktionen, die Funktionen als Argument
haben oder als Ergebnis liefern. Diese nennt man Funktionen
höherer Ordnung.
Davon kennen wir bisher Beispiele wie map oder (.)
Was man insgesamt damit machen kann, ist damit noch lange
nicht erschöpft ...
Gestaffelte
Funktionen
Higher Order
Functions
“Funktionen als Bürger erster Klasse”
Universität Bielefeld
Programmieren
in Haskell
Giegerich
LambdaExpressions
Funktionen definieren kann man in jeder Programmiersprache.
Eine funktionale Programmiersprache erlaubt auch
Funktionen in Datenstrukturen, z.B. [(+), (-), (*)]
Funktionen als Argmente, z.B. map square [1..100]
Funktionen als Ergebnisse, z.B. f . g
Gestaffelte
Funktionen
Higher Order
Functions
Wie entstehen Funktionen?
Benannte Funktionen werden vom Programmierer definiert.
Unbenannte Funktionen (engl. anonymous functions) entstehen
Universität Bielefeld
Programmieren
in Haskell
Giegerich
LambdaExpressions
durch teilweise Anwendung benannter Funktionen, z.B.
(3+), (‘div‘ 2), ("niemals"++)
map reverse
Note ce
(‘Note‘ (1/4))
map (1:)
all_equal 3 4
Natürlich kann man diesen dann auch Namen geben:
startnie = ("niemals"++)
durch Verknüpfung von Funktionen, z.B.
reverse . concat
oder durch ...
Gestaffelte
Funktionen
Higher Order
Functions
Lambda-Abstraktion
Universität Bielefeld
Programmieren
in Haskell
Abstraktion verwandelt einen Ausdruck in eine Funktion
3 ∗ a − b2 + 5
λa, b → 3 ∗ a − b 2 + 5
Giegerich
ist ein Ausdruck
LambdaExpressions
ist eine Funktion von a und b
Gestaffelte
Funktionen
(λa, b → 3 ∗ a − b 2 + 5)(2, 3) ist eine Funktionsanwendung und berechnet den Wert 2
Higher Order
Functions
Abstraktion: Vom konkreten zum Allgemeinen Dur3Klang
cDur3kl = Note ce (1/4) :+: Note eh (1/4) :+: Note ge (1/4)
versus
dur3kl = \(t,d) -> Note t d :+: Note (t+4) d :+: Note (t+7) d
Auf der rechten Seite der Gleichung steht eine anonyme
Funktion (die links einen Namen bekommt)
Lambda-Expressions in Haskell
Universität Bielefeld
Programmieren
in Haskell
Man nennt sie ...
Lambda-Abstractions
Lambda-Ausdrücke
Anonyme Funktionen
Example
1
2
3
4
(\ a b c - > b ^2 -4* a * c ) 1 5 4
map (\ x -> x ^2 + 1) [1..10]
filter (\ x -> x ‘mod ‘ 5 == 0) [1..100]
map (\( x , y ) -> x + y ) ( zip [1..10] [1..10])
Siehe online Beispiele ...
Giegerich
LambdaExpressions
Gestaffelte
Funktionen
Higher Order
Functions
Syntax
Universität Bielefeld
Programmieren
in Haskell
Lambda-Ausdruck
Einfache und allgemeine Form:
Giegerich
λx1 . . . xn → expr
(1)
LambdaExpressions
λpat1 . . . patn → expr
(2)
Gestaffelte
Funktionen
Higher Order
Functions
1
2
Bzw: in Haskell
\ x1 ... xn -> expr
\ pat1 ... patn -> expr
definiert eine anonyme, n-stellige Funktion.
Wichtig:
Lambda-Ausdruck ist ein Ausdruck, sein Wert eine Funktion.
Er kann überall stehen, wo Ausdrücke erlaubt sind,
z.B. als Argument einer Funktion/ eines Lambda-Ausdrucks
Wiederholung: Pattern/Muster
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Pattern sind Ausdrücke bestehend aus
LambdaExpressions
natürlichen Zahlen
Gestaffelte
Funktionen
Werten
Higher Order
Functions
Variablen
Datentyp-Konstruktoren
Wildcard-Symbol: _
aber ohne Funktionen!! (– mit Ausnahme des Musters n+1)
Lambda-Patterns
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Lambda-Patterns erlauben pattern matching auf der “linken”
Seite auch in anonymen Funktionen.
Muster von mehrstelligen Konstruktoren müssen geklammert
werden:
Example
1
(\( x : xs ) -> x ) [4 ,3 ,2]
LambdaExpressions
Gestaffelte
Funktionen
Higher Order
Functions
Currying: 1-stellige versus n-stellige Funktionen
Universität Bielefeld
n-stellige Funktionen lassen sich stets als 1-stellige Funktionen
auffassen.
Man nennt sie dann “curried” oder auf Deutsch
Programmieren
in Haskell
Giegerich
LambdaExpressions
curryfiziert (nach Haskell B. Curry)
geschönfinkelt (nach Moses Schönfinkel)
schön gefinkelt ???
gestaffelt
Gestaffelte
Funktionen
Higher Order
Functions
Gestaffelte Sichtweise
λpat1 pat2 . . . patn → expr
(3)
λpat1 → λpat2 → . . . λpatn → expr
(4)
(λpat1 → (λpat2 → . . . (λpatn → expr)))
(5)
Bzw:
Beispiel: sechsmal die gleiche Funktion
Example
1
2
> add : : I n t −> I n t −> I n t
> add x y = x + y
3
4
5
> add ’ : : I n t −> ( I n t −> I n t )
> add ’ x y = x + y
6
7
8
> add ’ ’ : : I n t −> ( I n t −> I n t )
> add ’ ’ x = \ y −> x + y
9
10
11
> add ’ ’ ’ : : I n t −> ( I n t −> I n t )
> add ’ ’ ’ = \ x y −> x + y
12
13
14
> add ’ ’ ’ ’ : : I n t −> ( I n t −> I n t )
> add ’ ’ ’ ’ = \ x −> \ y −> x + y
15
16
17
> add ’ ’ ’ ’ ’ : : I n t −> I n t −> I n t
> add ’ ’ ’ ’ ’ = \ x −> \ y −> x + y
Universität Bielefeld
Programmieren
in Haskell
Giegerich
LambdaExpressions
Gestaffelte
Funktionen
Higher Order
Functions
Beispiele
Universität Bielefeld
Programmieren
in Haskell
Giegerich
LambdaExpressions
Gestaffelte
Funktionen
Typen: Was meint Hugs?
Higher Order
Functions
Currying
Universität Bielefeld
Programmieren
in Haskell
Giegerich
LambdaExpressions
die verschiedenen Versionen von add haben den gleichen
Typ
→ Denn -> (function type constructor) ist rechtsassoziativ in
Haskell
Sichtweise: Das “zweistellige” add ist eine einstellige
Funktion, die eine einstellige Funktion als Ergebnis hat
Gestaffelte
Funktionen
Higher Order
Functions
Partielle Anwendung ohne Lambda
Universität Bielefeld
Programmieren
in Haskell
Giegerich
LambdaExpressions
Example
1
2
(1+)
map (1+) [2 ,3 ,4]
Gestaffelte
Funktionen
Higher Order
Functions
Kein Curry?
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Example
1
f (a , b ) = a + b
LambdaExpressions
Gestaffelte
Funktionen
Ausweg:
1
2
curry
curry f x y
Higher Order
Functions
:: (( a , b ) -> c ) -> a -> b -> c
= f (x , y )
Zuviel Curry?
1
2
uncurry
uncurry f p
:: ( a -> b -> c ) -> (( a , b ) -> c )
= f ( fst p ) ( snd p )
Fazit: Currying
Universität Bielefeld
Programmieren
in Haskell
Giegerich
In Haskell:
Nomen est omen
Funktionen sind “per-default” gestaffelt zurückgibt
Staffelung vermeidbar via Tupel
Funktions-Typkonstruktor (->) ist rechtsassoziativ
Functions-Anwendung ist linksassoziativ:
f a b ist das gleiche wie (f a) b
So gesehen ist jede mehrstellige Funktion in Haskell eine
Higher-Order-Function.
LambdaExpressions
Gestaffelte
Funktionen
Higher Order
Functions
Higher Order Functions
Universität Bielefeld
Programmieren
in Haskell
Funktionen höherer Ordnung
Funktionen mit Funktionen als Parameter
Funktionen mit Funktionen als Rückgabewert
Giegerich
LambdaExpressions
Gestaffelte
Funktionen
Higher Order
Functions
In Haskell:
Funktionen als “1st class citizens” sind oft von höherer
Ordnung
Funktionen höherer Ordnung sind “Kontrollstrukturen”,
die man (im Unterschied zu imperativen Sprachen) selbst
definieren kann
Wiederholung
Universität Bielefeld
Programmieren
in Haskell
Giegerich
1
2
3
Bekannte
map ::
map
map
Beispiele für Funktionen höherer Ordnung:
(a - > b ) -> [ a ] -> [ b ]
f
[]
= []
f
( x : xs ) = f x : map f xs
4
5
6
filter :: ( a -> Bool ) -> [ a ] -> [ a ]
filter p xs = [ x | x <- xs , p x ]
LambdaExpressions
Gestaffelte
Funktionen
Higher Order
Functions
Wiederholung A&D: Strukturelle Rekursion
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Srukturelle Rekursion auf Listen –
Schema:
f
LambdaExpressions
:: [σ] -> τ
f []
=
e1
f (a : as)
=
e2
where s
=
f as
wobei e1 und e2 Ausdrücke vom Typ τ sind und e2 die
Variablen a, as und s (nicht aber f ) enthalten darf.
Gestaffelte
Funktionen
Higher Order
Functions
Beispiele
Universität Bielefeld
Programmieren
in Haskell
length
Giegerich
sum
LambdaExpressions
prod
Gestaffelte
Funktionen
and
1
2
Alle folgen dem gleichen Schema wie z.B.
and [] = True
and ( x : xs ) x && ( and xs )
Funktion Terminierungs- Anwendung
wert (e1)
auf x (e2)
length
0
1
sum
0
x
prod
1
x
and
True
x
Higher Order
Functions
Akkumulation
des Ergebnisses (e2)
+
+
*
&&
Schema für diese Art der strukturellen Rekursion in
Haskell
Universität Bielefeld
Programmieren
in Haskell
Giegerich
foldr steht für “fold right”
LambdaExpressions
1
2
3
4
foldr :: ( a -> b -> b ) -> b -> [ a ] -> b
foldr _ e []
= e
foldr f e ( x : xs ) = f x ( foldr f e xs )
5
6
7
8
9
10
sum
prod
and
or
length
=
=
=
=
=
foldr
foldr
foldr
foldr
foldr
(+) 0
(*) 1
(&&) True
(||) False
(\ a b -> 1 + b ) 0
Gestaffelte
Funktionen
Higher Order
Functions
Noch mehr Beispiele mit foldr ...?
Universität Bielefeld
Programmieren
in Haskell
Giegerich
LambdaExpressions
Gestaffelte
Funktionen
1
2
x ++ y = foldr (:) y x
concat = foldr (++) []
Higher Order
Functions
foldr — sum aus der Vogelperspektive
Universität Bielefeld
foldr f e ersetzt alle : durch f und [] durch e.
Programmieren
in Haskell
Example
Giegerich
[1..5] = [1,2,3,4,5] = 1:2:3:4:5:[]
:
1
LambdaExpressions
+
1
:
2
:
3
⇒
foldr (+) 0
:
4
:
5 []
Gestaffelte
Funktionen
Higher Order
Functions
+
2
+
3
+
4
+
5 0
foldr — prod aus der Vogelperspektive
Universität Bielefeld
Programmieren
in Haskell
∗
:
Giegerich
1
2
∗
1
:
Gestaffelte
Funktionen
∗
2
:
LambdaExpressions
Higher Order
Functions
⇒
3
4
∗
3
:
∗
4
:
5 []
5 1
→ (1 ∗ (2 ∗ (3 ∗ (4 ∗ (5 ∗ 1)))))
(6)
foldr — length
Universität Bielefeld
:
Programmieren
in Haskell
+
Giegerich
1
:
2
+
1
:
LambdaExpressions
Gestaffelte
Funktionen
+
1
Higher Order
Functions
⇒
3
:
4
+
1
:
5 []
1
+
1 0
→ (1 + (1 + (1 + (1 + (1 + 0)))))
(7)
“Rechenstrategie” Vogelperspektive
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Vogelperspektive bzgl. f bedeutet:
Rechne so viel (und NUR so viel) bis alle Auftreten von f
verschwunden sind.
das ist i.A. werder inner- noch outermost
aber auf jeden Fall mathematisch korrekt.
Dient nicht dem wirklichen Rechnen, aber dem
Programmverstehen.
LambdaExpressions
Gestaffelte
Funktionen
Higher Order
Functions
foldr klammert von rechts ...
Universität Bielefeld
Programmieren
in Haskell
Giegerich
LambdaExpressions
1
2
3
length " acbde " = > ( 1 + ( 1 + ( 1 + ( 1 + ( 1 + 0 ) ) ) ) )
and [ True , False , True ] = >
True && ( False && ( True && False ))
... geht’s auch von links her?
Gestaffelte
Funktionen
Higher Order
Functions
foldl
Universität Bielefeld
Programmieren
in Haskell
:
f
4
f
3
f
f
e 1
2
1
5
f
:
2
foldl
⇐
Giegerich
f
1
:
3
foldr
⇒
:
4
:
5 []
LambdaExpressions
f
Gestaffelte
Funktionen
Higher Order
Functions
f
2
f
3
4
f
5 e
foldl-Definition
Universität Bielefeld
Programmieren
in Haskell
Giegerich
LambdaExpressions
1
2
3
foldl :: ( b -> a -> b ) -> b -> [ a ] -> b
foldl f e []
= e
foldl f e ( x : xs ) = foldl f ( f e x ) xs
Der Endwert e ist jetzt ein Startwert.
Gestaffelte
Funktionen
Higher Order
Functions
Unterschiede foldl, foldr
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Unterschiedliche Faltung
LambdaExpressions
foldr klammert rechtsassoziativ:
((a1 · (a2 · (a3 · . . . · (an · e)))))
(8)
Gestaffelte
Funktionen
Higher Order
Functions
foldl klammert linksassoziativ:
(((((e · a1 ) · a2 ) · a3 ) · . . .) · an )
(9)
die Faltung von foldr entspricht der Datentypdefinition
ggf. Platzbedarf besser bei foldl
Unterschiede foldl, foldr
Universität Bielefeld
Programmieren
in Haskell
Giegerich
LambdaExpressions
Eigenschaft
1
foldr f e ( reverse x ) = foldl ( flip f ) e x
Beispiel siehe Tafel.
Gestaffelte
Funktionen
Higher Order
Functions
foldl Beispiele
Universität Bielefeld
Programmieren
in Haskell
Giegerich
LambdaExpressions
sum
= foldr (+) 0
or = foldr (||) False
concat = foldl (++) []
...
Gestaffelte
Funktionen
Higher Order
Functions
Fold im Allgemeinen
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Fold-Operationen verarbeiten rekursive Datentypen nach deren
Rekursionsschema, also mit struktureller Rekursion
Kompakter Code
Vermeidung von Redundanz
Angabe des Wesentlichen
Vermeidung von Fehlern bei hand-programmierter
Rekursion
LambdaExpressions
Gestaffelte
Funktionen
Higher Order
Functions
Weitere Beispiele?
Universität Bielefeld
Programmieren
in Haskell
Giegerich
1
2
3
4
Die Funktion tabulate verwandelt Funktionen über einem
LambdaExpressions
gegebenen Intervall in tabellierende Funktionen
Gestaffelte
> tabulate :: ( Int , Int ) - >( Int - > a ) - >( Int - > a )
Funktionen
Higher Order
> tabulate ( lo , up ) f = ( t !) where
Functions
>
t = array ( lo , up ) ( zip domain ( map f domain ))
>
domain = [ lo .. up ]
1
2
> g = tabulate f
was sich beim mehrfachen Aufruf von g anstelle von f bezahlt
macht.
Combinator Languages, eDSLs
Universität Bielefeld
Programmieren
in Haskell
Funktionen höherer Ordnung, deren Argumente nur Funktionen
sind, nennt man “Kombinatoren”.
In manchen Anwendungen gibt es eine Anzahl komplexer,
spezifischer Operationen, die sich zu Kombinatoren
abstrahieren lassen.
Beispiele: Parsing, Prettyprinting, Dynamic Programming,
Format-Transformationen
Dies führt zur Definition von anwendungs-spezifischen
Sprachen, “eingebettet” in Haskell (embedded domain
specific languages, eDSLs)
Dies ist der schnellste Weg zu einer DSL, aber nicht der
Weg zur schnellsten DSL
Giegerich
LambdaExpressions
Gestaffelte
Funktionen
Higher Order
Functions
Zusammenfassung
Universität Bielefeld
Programmieren
in Haskell
Giegerich
Wann definiert man selbst neue Funktioen höherer Ordnung?
Wenn sich die gleiche Verknüpfung anderer Funktionen
mehrfach wiederholt, ...
... macht die Verwendung von Kombinatoren die
Systematik transparent,
hilft beim Vermeiden von Fehlern,
und erhöht so die eigene Programmier-Produktivität
LambdaExpressions
Gestaffelte
Funktionen
Higher Order
Functions
Herunterladen