Programmieren in Haskell Pattern Matching

Werbung
Programmieren in Haskell
Pattern Matching
Peter Steffen
Universität Bielefeld
Technische Fakultät
23.01.2009
1
Programmieren in Haskell
Pattern matching
Eine Funktion kann mehrere Definitionen haben. Es wird per Pattern
Matching entschieden – anhand der aufrufenden Parameter, welche
Definition angewendet wird. Zum Beispiel:
sum []
= 0
sum (x:xs) = x + sum xs
Wenn sum mit der leeren Liste aufgerufen wird, wird die erste Definition
angewendet. Wenn die Liste nicht leer ist, die zweite. In diesem Fall wird
das erste Element der Liste an die Variable x gebunden und der Rest der
Liste an die Variable xs.
2
Programmieren in Haskell
As-pattern
Als Beispiel hier eine Funktion, die das erste Element einer Liste dupliziert:
f (x:xs) = x:x:xs
Diese können wir alternativ auch wie folgt aufschreiben:
f s@(x:xs) = x:s
Hier wird x an das erste Element der Liste gebunden, xs an die Restliste
und s an die komplette Liste.
3
Programmieren in Haskell
Wildcards
Wildcards sind nützlich, wenn wir gegen einen Ausdruck matchen wollen,
dessen Komponenten uns nicht interessieren. Zum Beispiel:
head (x:_) = x
tail (_:xs) = xs
Hier matchen die Ausdrücke jeweils eine nicht-leere Liste. Die Wildcards
matchen dabei alles.
4
Programmieren in Haskell
Lazy Pattern
Ein Lazy Pattern pat hat die Form ∼pat
Lazy Pattern sind unabweisbar, d.h. das Matchen eines Werts v gegen
ein Pattern ∼pat ist immer erfolgreich, unabhängig von pat.
Ein Wert auf der rechten Seite einer Definition wird erst dann an das
Pattern gebunden, wenn er wirklich benötigt wird. Wenn der
Eingabewert nicht auf das Pattern matcht, gibt es in diesem Fall
einen Fehler.
5
Programmieren in Haskell
Ein einfaches Beispiel
liste :: [Int] -> [Int]
liste (x:xs) = 1:liste xs
Main> liste [1..10]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1
Program error: pattern match failure: liste []
Hier gibt es einen Fehler, weil das Muster x:xs nicht erfolgreich an die
leere Liste gebunden werden kann.
Mit einem lazy pattern:
liste :: [Int] -> [Int]
liste ∼(x:xs) = 1:liste xs
Main> liste [1..10]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, . . .
In diesem Fall gibt es keinen Fehler, weil die Werte der Variablen x und xs
gar nicht benötigt werden, und diese durch das lazy pattern nicht an die
Eingabe gebunden werden.
6
Programmieren in Haskell
Ein größeres Beispiel
Es soll die Interaktion zwischen einem Server-Prozess server und
einem Client-Prozess client simuliert werden.
client sendet eine Sequenz von requests (Anfragen) an den Server,
und der Server antwortet auf jede Anfrage mit einer response
(Antwort).
In Haskell:
reqs = client init resps
resps = server reqs
7
Programmieren in Haskell
Ein größeres Beispiel
reqs = client init resps
resps = server reqs
Der Client und der Server haben die folgende Struktur:
client init (resp:resps) = init : client (next resp) resps
server
(req:reqs)
= process req : server reqs
next sei dabei eine Funktion, die anhand einer Antwort des Servers die
nächste Anfrage generiert, und process sei eine Funktion, die eine
Anfrage des Clients bekommt, diese verarbeitet und eine passende Antwort
generiert.
Problem: Dieses Programm terminiert nicht! Das liegt daran, das client
versucht die Response-Liste zu matchen, bevor es seine erste Anfrage
gesendet hat.
8
Programmieren in Haskell
Lösung
Eine Möglichkeit, dieses Problem zu lösen, ist folgende:
client init resps = init : client (next (head resps)) (tail resps)
In diesem Fall matcht client immer erfolgreich, und auf die Liste resps
wird erst zugegriffen, wenn diese wirklich verwendet wird.
Eine besser lesbare Version bekommen wir allerdings, wenn wir ein lazy
pattern verwenden:
client init ∼(resp:resps) = init : client (next resp) resps
Auch hier wird erst auf resp und resps zugegriffen, wenn diese wirklich
verwendet werden. In diesem Fall kann die erste Anfage losgeschickt
werden, woraufhin auch die erste Antwort generiert werden kann.
9
Programmieren in Haskell
Test
Um das Programm zu vervollständigen, müssen noch die folgenden drei
Funktionen definiert werden:
init
= 0
next resp
= resp
process req = req+1
Ohne das lazy pattern ergibt sich folgendes:
Main> take 10 reqs
ERROR - Control stack overflow
Mit dem lazy pattern funktioniert es:
Main> take 10 reqs
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
10
Programmieren in Haskell
Herunterladen