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