Operationale Semantik für List Comprehensions Definition Semantik

Werbung
8 · Listenverarbeitung
List Comprehensions · 8.5
Operationale Semantik für List Comprehensions
Die Semantik der List Comprehensions, welche wir vorhin (cf. Seite 237)
eher durch “hand-waving” erklärt haben, lässt sich —ganz ähnlich wie bei
der β-Reduktion des λ-Kalküls— durch Reduktionsregeln formal erklären.
Definition Semantik der List Comprehension, ohne Pattern Matching
Sei e ein Haskell-Ausdruck, v Variable, qs eine Sequenz32 von Qualifiern.
Die folgenden Regeln reduzieren jeweils den ersten Qualifier:
○
1
[ e | v <- [], qs ] � []
[ e | v <- (x : xs), qs ] � [ e | qs ][v � x]
2
++ [ e | v <- xs, qs ] ○
[ e | False, qs ] � []
[ e | True, qs ] � [ e | qs ]
32 qs
[ e | ] � [ e ]
○
3
○
4
○
5
ist keine Haskell-Liste, sondern ein Konstrukt der Meta-Ebene, cf. Seite 237.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
243
8 · Listenverarbeitung
List Comprehensions · 8.5
�
Die ersten beiden Reduktionsregeln reduzieren einen Generator über einer
1 bzw. nichtleeren ○
2 Liste.
leeren ○
�
3 und ○
4 testen Prädikate.
Regeln ○
�
5 ist anwendbar, sobald die Sequenz der Qualifier vollständig
Regel ○
reduziert wurde.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
244
8 · Listenverarbeitung
Beispiel
List Comprehensions · 8.5
Reduktion von [ x^2 | x <- [1,2,3], odd x ]
[ x^2 | x <- [1,2,3], odd x ]
�
=
�
�
�
�
2
verwenden ○
[ x^2 | odd x ][x � 1] ++ [ x^2 | x <- [2,3], odd x ]
��
�
�
A
[ 1^2 | odd 1 ] ++ A
[ 1^2 | True ] ++ A
4
verwenden ○
[ 1^2 | ] ++ A
5
verwenden ○
[1^2] ++ A
A
�
��
�
1 : [ x^2 | x <- [2,3], odd x ]
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
245
8 · Listenverarbeitung
List Comprehensions · 8.5
1 : [ x^2 | x <- [2,3], odd x ]
�
=
�
�
�
�
�
=
2
verwenden ○
1 : [ x^2 | odd x ][x � 2] ++ [ x^2 | x <- [3], odd x ]
��
�
�
1 : [ 2^2 | odd 2 ] ++ B
B
1 : [ 2^2 | False ] ++ B
3
verwenden ○
1 : [] ++ B
B
�
��
�
1 : [ x^2 | x <- [3], odd x ]
2
verwenden ○
1 : [ x^2 | odd x ][x � 3] ++ [ x^2 | x <- [], odd x ]
1
links wie gehabt (� 9), und rechts verwenden wir ○
1 : 9 : []
[1, 9]
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
246
8 · Listenverarbeitung
List Comprehensions · 8.5
Abbildung von List Comprehensions auf den Haskell-Kern
�
Prinzipiell erlaubt das System der eben besprochenen Reduktionsregeln,
List Comprehensions auf in Haskell vordefinierte Funktionen
zurückzuführen.
�
Im Folgenden betrachten wir ein Übersetzungsschema �·�, das List
Comprehensions aus Haskell-Code entfernt33 :
�
� Code mit List Comprehension � = Code ohne List Comprehension
�·� kann vom Compiler auf Haskell-Quellcode angewandt werden, um
äquivalenten Code ohne List-Comprehensions zu erhalten.
33 Hier
verwenden wir semantische Klammern etwas anders als gewohnt.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
247
8 · Listenverarbeitung
1
2
List Comprehensions · 8.5
Dabei basiert das Schema �·� auf der Funktion concatMap:
concatMap :: (α -> [β]) -> [α] -> [β]
concatMap f = foldr (\x xs -> f x ++ xs) []
Frage Was tut diese Funktion?
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
248
8 · Listenverarbeitung
1
2
List Comprehensions · 8.5
concatMap :: (α -> [β]) -> [α] -> [β]
concatMap f = foldr (\x xs -> f x ++ xs) []
3
4
5
> concatMap (replicate 3) "hello"
"hhheeellllllooo"
Antwort concatMap f xs wendet die Funktion f auf jedes Element von xs
an. Dabei gibt f jeweils eine Liste zurück, welche von concatMap
konkateniert werden.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
249
8 · Listenverarbeitung
List Comprehensions · 8.5
Übersetzungsschema �·�
immer noch ohne Pattern Matching
Sei e ein Ausdruck, v eine Variable, b ein Boolescher Ausdruck, und qs
wieder eine Sequenz von Qualifiern.
�[ e | v <- xs, qs ]� = concatMap (λv . �[ e | qs ]�) �xs�
○
1
2
�[ e | b, qs ]� = if �b� then �[ e | qs ]� else [] ○
�[ e | ]� = [ �e� ]
�e� = Wende �·� rekursiv auf alle nicht-primitiven
○
3
○
4
Teilausdrücke von e an.
�
�
1 : Generatoren; ○
2 : Prädikate)
Wieder wird die Sequenz der Qualifier (○
○
reduziert, bis 3 den Fall ohne Qualifier behandeln kann.
4 steigt rekursiv im AST ab, um evtl. weitere List
Regel ○
Comprehensions zu übersetzen.
2 , statt einfach b zu schreiben?
Frage Wozu brauchen wir �b� in Regel ○
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
250
8 · Listenverarbeitung
List Comprehensions · 8.5
Beispiel Übersetzung von [ x^2 | x <- [1..5], odd x ]
=
=
=
=
�[ x^2 | x <- [1..5], odd x ]�
○
1
concatMap (λx. �[ x^2 | odd x ]�) �[1..5]�
○
4
concatMap (λx. �[ x^2 | odd x ]�) [1..5]
○
2
concatMap (λx. if �odd x� then �[ x^2 | ]� else []) [1..5]
○
3 und 2 × ○
4
concatMap (λx. if odd x then [ x^2 ] else []) [1..5]
Frage Bisher haben wir Pattern Matching in unserem
Übersetzungsschema �·� nicht berücksichtigt. Wo müssen wir
nachbessern?
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
251
8 · Listenverarbeitung
List Comprehensions · 8.5
Pattern Matching in List Comprehensions
Beispiel
1
Was tut die Funktion heads? Was ist der Typ?
heads xs = [ y | (y:_) <- xs ]
Übersetzen heads mit dem Übersetzungsschema �·�:
=
=
=
�λxs. [ y | (y:_) <- xs ]�
○
4
λxs. �[ y | (y:_) <- xs ]�
○
1 , behandeln Pattern wie Variable
�
�
λxs. concatMap λ(y:_). �[ y | ]� �xs�
○
3,○
1
�
�
λxs. concatMap λ(y:_). [y] xs
�
�
�
η concatMap λ(y:_). [y]
�
�
Frage Gilt heads ≡ concatMap λ(y:_). [y] ?
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
252
8 · Listenverarbeitung
List Comprehensions · 8.5
Antwort Nein: Wenn der Pattern Match gegen (y:_) fehlschlägt, wird das
Programm abgebrochen!
1
2
3
4
> heads [[1],[2,3],[4,5,6]]
[1,2,4]
> concatMap (\(y:_)-> [y]) [[1],[2,3],[4,5,6]]
[1,2,4]
5
6
7
8
9
> heads [[1],[],[4,5,6]]
[1,4]
> concatMap (\(y:_)-> [y]) [[1],[],[4,5,6]]
[1*** Exception: <interactive>:23:12-23: Non-exhaustive patterns in lambda
Frage An welcher Stelle können wir das fixen?
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
253
8 · Listenverarbeitung
List Comprehensions · 8.5
Definition Übersetzungsschema �·� mit Pattern Matching
Sei e ein Ausdruck, p ein Pattern, v eine neue Variable, b ein Boolescher
Ausdruck, und qs wieder eine Sequenz von Qualifiern.
�
○
1
�[ e | p <- xs, qs ]� = concatMap λv . case v of
p → �[ e | qs ]� �
_ → []
�xs�
2
�[ e | b, qs ]� = if �b� then �[ e | qs ]� else [] ○
�[ e | ]� = [ �e� ]
�e� = Wende �·� rekursiv auf alle nicht-primitiven
○
3
○
4
Teilausdrücke von e an.
�
�
�
Variable v matcht gegen jeden Wert aus �xs� (cf. Seite 151),
case prüft dann ob v auf Pattern p matcht (cf. Seite 157),
für fehlgeschlagene Matches werden keine Ergebnisse erzeugt.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
254
8 · Listenverarbeitung
Beispiel
List Comprehensions · 8.5
Nochmal: heads xs = [ y | (y:_) <- xs ]
Übersetzen heads mit dem Übersetzungsschema �·�:
=
=
�λxs. [ y | (y:_) <- xs ]�
○
4
λxs. �[ y | (y:_) <- xs ]�
○
1 , diesmal richtig
�
λxs. concatMap λv . case v of
(y:_) → �[ y | ]� �
_ → []
�xs�
○
3,○
1
=
�
�
λxs. concatMap λv . case v of { (y:_) → [y]; _ → [] } xs
�
�
�
η concatMap λv . case v of { (y:_) → [y]; _ → [] }
Jetzt gilt:
�
�
heads ≡ concatMap λv . case v of { (y:_) → [y]; _ → [] }
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
255
8 · Listenverarbeitung
List Comprehensions · 8.5
Weiterführende Literatur
Richard Bird, and Philip Wadler.
Introduction to Functional Programming using Haskell.
Prentice Hall International, Series in Computer Science, 1998.
Jeroen, Fokker.
Functional Programming.
Department of Computer Science, Utrecht University, 1995.
http://www.staff.science.uu.nl/~fokke101/courses/fp-eng.pdf
Torsten Grust, and Marc H. Scholl.
How to Comprehend Queries Functionally.
Journal of Intelligent Information Systems, vol. 12, p. 191–218, 1999.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
256
9
Algebraische Datentypen
9 · Algebraische Datentypen
Dieses Kapitel erweitert Haskells Typsystem, das neben Basistypen
Integer, Float, Char, Bool, ...) und den Typkonstruktoren ([ · ], (,)...
und ->) auch algebraische Datentypen kennt.
�
Ganz analog zum Typkonstruktor [ · ], der die beiden
Konstruktorfunktionen (:) und [] einführte, um Werte des Typs [α]
zu konstruieren, kann der Programmierer neue Konstruktoren
definieren, um Werte eines neuen algebraischen Datentyps zu erzeugen.
�
Wie bei Listen und Tupeln möglich, können Werte dieser neuen Typen
dann mittels Pattern Matching wieder analysiert (dekonstruiert)
werden.
In der Tat ist der eingebaute Typkonstruktor [α] selbst ein algebraischer
Datentyp (s. unten).
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
258
9 · Algebraische Datentypen
9.1
Deklaration eines algebraischen Datentyps · 9.1
Deklaration eines algebraischen Datentyps
Mittels einer data-Deklaration wird ein neuer algebraischer Datentyp
spezifiziert mit:
� dem NamenT des Typkonstruktors (Identifier beginnend mit Zeichen
∈ {A...Z}) und seinen Typparametern αj ,
� den Namen Ki der Konstrukturfunktionen (Identifier beginnend mit
Zeichen ∈ {A...Z}) und der Typen βik , die diese als Parameter erwarten.
Syntax einer data-Deklaration
mit n ≥ 0, m ≥ 1, ni ≥ 0, die βik sind entweder Typbezeichner oder βik = αj :
data T α1 α2 ... αn
=
|
..
.
|
K1 β11 ... β1n1
K2 β21 ... β2n2
Km βm1 ... βmnm
Dieses data-Statement deklariert einen Typkonstruktor T und m
Konstruktorfunktionen:
Ki :: βi1 → ... → βini → T α1 α2 ... αn
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
259
9 · Algebraische Datentypen
Deklaration eines algebraischen Datentyps · 9.1
Sonderfälle
1. Die Ki haben keine Argumente, also ni = 0, n = 0.
data T = K1 | K2 | · · · | Km
T ist damit ein reiner Summentyp (auch: Aufzählungstyp) wie aus
vielen Programmiersprachen bekannt (etwa in C: enum).
• Die Konstruktoren haben alle den gleichen Typ, und bilden den Wertevorrat
von T :
Ki :: T
• Der Typ Bool ist ein Aufzählungstyp:
data Bool = False | True
• Dies gilt theoretisch ebenso für die anderen Basisdatentypen in Haskells
Typsystem:
1
2
data Int = -2^63 | ... | -1 | 0 | 1 | ... | 2^63-1 — Pseudo-Code!
data Char = ’a’ | ’b’ | ... | ’A’ | ... | ’1’ | ...
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
260
9 · Algebraische Datentypen
Deklaration eines algebraischen Datentyps · 9.1
2. Es gibt nur eine Konstruktorfunktion, also m = 1, β1i = αi .
data T α1 ... αn = K1 α1 ... αn
T verhält sich damit ähnlich wie der Tupelkonstruktor und wird auch
Produkttyp genannt. In der Typtheorie oft als β11 × β12 × · · · × β1n1
notiert.
• Die (fest eingebauten) Definitionen von Tupeln entsprechen also:
data (α,β)
= (,) α β
data (α,β,γ) = (,,) α β γ
Allgemein führt die data-Deklaration also Alternativen (Summe) von
Produkttypen ein, bezeichnet als sum-of-product types.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
261
9 · Algebraische Datentypen
Deklaration eines algebraischen Datentyps · 9.1
Arbeiten mit Aufzählungstypen
Beispiel
Der benutzerdefinierte Aufzählungstyp
data Weekday = Mon | Tue | Wed | Thu | Fri | Sat | Sun
definiert
�
den Typkonstruktor Weekday und
�
die Konstruktorfunktionen Mon, ..., Sun mit z.B. Mon :: Weekday.
Funktionen über algebraischen Datentypen werden mittels Pattern
Matching realisiert:
1
2
3
4
weekend
weekend
weekend
weekend
:: Weekday -> Bool
Sat = True
Sun = True
_
= False
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
262
9 · Algebraische Datentypen
Deklaration eines algebraischen Datentyps · 9.1
Ausgabe von Aufzählungstypen
Bei der Arbeit mit diesen neuen Typen reagiert Haskell merkwürdig:
1
2
3
4
5
6
7
8
9
*Main> Mon
<interactive>:7:1:
No instance for (Show Weekday) arising from a use of ‘print’
In a stmt of an interactive GHCi command: print it
*Main> Tue == Fri
<interactive>:8:5:
No instance for (Eq Weekday) arising from a use of ‘==’
In the expression: Tue == Fri
In an equation for ‘it’: it = Tue == Fri
1. Das Haskell-System hat keine Methode show für die Ausgabe von
Werten des Typs Weekday mitgeteilt bekommen.
• Intuition: Name des Konstruktors Ki benutzen.
2. Gleichheit auf den Elementen des Typs ist nicht definiert.
• Intuition: nur Werte die durch denselben Konstruktor Ki mit identischen
Parametern erzeugt wurden, sind gleich.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
263
9 · Algebraische Datentypen
�
Deklaration eines algebraischen Datentyps · 9.1
Haskell kann diese Intuitionen automatisch zur Verfügung stellen,
wenn die data-Deklaration durch den Zusatz
deriving (Show, Eq)
erweitert wird.
1
2
data Weekday = Mon | Tue | Wed | Thu | Fri | Sat | Sun
deriving (Show, Eq)
�
Der neue Typ T wird damit automatisch Instanz der Typklasse Show aller
druckbaren Typen und Instanz der Typklasse Eq aller Typen mit
Gleichheit (==).
�
Der deriving-Mechanismus ist genereller und wird später noch genauer
besprochen.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
264
9 · Algebraische Datentypen
Deklaration eines algebraischen Datentyps · 9.1
Maybe an Integer?
Algebraische Datentypen erlauben die Erweiterung eines Typs um einen
speziellen Wert, der eingesetzt werden kann, wenn Berechnungen kein
sinnvolles oder ein unbekanntes Ergebnis besitzen.
Beispiel
1
2
3
Erweitere den Typ Integer um einen “Fehlerwert” None:
data MaybeInt = Val Integer
| None
deriving (Show, Eq)
4
5
6
7
safediv
:: Integer -> Integer -> MaybeInt
safediv _ 0 = None
safediv x y = Val (x ‘div‘ y)
Fragen
�
Was sind in diesem Beispiel Konstruktorfunktionen?
�
Wie lautet jeweils ihr Typ?
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
265
9 · Algebraische Datentypen
Deklaration eines algebraischen Datentyps · 9.1
Maybe α
�
Der vordefinierte Typkonstruktor Maybe kann jeden Typ um das Element
Nothing erweitern.
data Maybe α = Just α
| Nothing
deriving (Eq, Show)
�
Der Typkonstruktor ist polymorph (wie etwa auch [α]):
Beispiel Erweitere den Typ Integer um einen “Fehlerwert” Nothing:
1
2
3
safediv
:: Integer -> Integer -> Maybe Integer
safediv _ 0 = Nothing
safediv x y = Just (x ‘div‘ y)
Fragen
�
Welchen Typ konstruiert der Typkonstruktor? Woraus?
�
Was sind die Typen der Konstruktorfunktionen?
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
266
Herunterladen