Pattern Matching in ML

Werbung
Pattern Matching in ML
1
Pattern Matching in ML
Motivation
ML ist eine funktionale Programmiersprache und insofern in manchen Dingen
etwas unhandlich, wenn man in diesem Paradigma bleiben möchte. Inbesondere, wenn viele Fallentscheidungen anfallen, wird der Code schnell unübersichtlich; denkt nur an diese Funktion, die auf Aufgabenblatt 1 mathematisch
formuliert war (und ohne Weiteres in ML umsetzbar ist):
fun has_to_or_three ( l : ’a list ) : bool =
if ( null(l) )
then false
else if ( null(tl(l)) )
then false
else if ( null(tl(tl(l))) )
then true
else null(tl(tl(tl(l))));
Man stelle sich nun vor, wie das aussähe, wenn auf 10 Elemente zu prüfen
wäre.
Ein anderes kaum lesbares Beispiel liefert das Aufgabenblatt zwei, wo aus einer
dreielementigen Liste die zwei größten zurückzugeben waren:
fun two_biggest ( l : int list ) : int list =
if
hd(l) > hd(tl(l))
then if
hd(tl(l)) > hd(tl(tl(l)))
then [hd(l), hd(tl(l))]
else [hd(l), hd(tl(tl(l)))]
else if
hd(l) > hd(tl(tl(l)))
then [hd(l), hd(tl(l))]
else [hd(tl(l)), hd(tl(tl(l)))];
Diese Beispiele sind so sicherlich in vielerlei Hinsicht ungeschickt implementiert; insbesondere wäre die zweite Aufgabe für allgemeine Listen wesentlich
angenehmer zu lösen gewesen. Ich denke aber, wir sind uns einig, wenn ich
sage, dass diese Funktionen nicht lesbar oder gar ästhetisch sind. Ich möchte
daher im Folgenden das Pattern Matching vorstellen, das ein sehr elegantes
Mittel liefert, um mit den nativen Datenstrukturen von ML umzugehen.
Was ist denn das nun?
Zunächst das Offensichtlichste: Pattern Matching ist Mustererkennung - was
eine Überraschung! Dahinter steckt genau das: In Werten werden Muster erkannt. Dazu gibt es Folgende Werkzeuge:
• Literale ( Zahlen für int, true/f alse für bool, Zeichen für char, [. . . ] für
Listen . . . )
• Konstruktoren ( :: für Listen, (·) für Tupel . . . )
• Bezeichner
Daraus könnt ihr nun Pattern zusammenbauen, im Folgenden einige Beispiele:
5
(true, 5)
(x :: rl,b)
x::y::z::[]
_
Raphael Reitzig, 10. November 2007
Pattern Matching in ML
2
Worauf matchen diese Pattern nun? Gehen wir sie durch:
• 5
Das Literal 5 matcht auf den Integerwert 5, welche Überraschung.
• (true, 5)
Hier matchen alle Tupel, die genau so aussehen. Auch langweilig.
• (x :: rl, b)
Dieses Pattern passt auf Tupel, deren erstes Element eine Liste ist (über
das zweite wird nichts gesagt). Dies ist an dem verwendeten Konstruktor
:: zu erkennen. Genauer gesprochen passt es auf solche Tupel, deren erstes
Element eine Liste mit mindestens einem Element ist - der Konstruktor
sagt: Da ist ein Element x, das von mir hätte auf die Liste rl gelegt
werden können! Dazu muss natürlich beides da sein - ein x und eine
Restliste, die auch die leere Liste sein kann.
• x :: y :: z :: []
Nach dem Beispiel eben sollte das klar sein: Hier passen alle Listen, die
genau drei Elemente haben.
•
Das ist ein Platzhalter für alles.
lässt alles rein.
Die Anwendung in ML
Das ist ja alles schön und gut, nur müsst ihr es auch anwenden können. Dazu
gibt es im Wesentlichen zwei Varianten, die ich an einem einfachen Algorithmus, der rekursiv die Elemente einer Integerliste aufaddiert, demonstrieren
möchte:
fun sum ( l : int list ) : int =
case l of
e::rl => e + sum(rl)
| []
=> 0;
fun sum ( e::rl : int list ) : int = e + sum(rl)
|
sum ( [] )
= 0;
Beide Funktionen leisten genau dasselbe. Die erstere Variante ist mehr dazu
gedachte, in einer Funktion errechnete Werte zu matchen, die untere für das
direkte Matchen von Funktionsparametern. Hierbei wird immer der der mit
| (oder) getrennten Fälle ausgeführt, auf den der untersuchte Wert passt. Ihr
seht, dass die im Pattern verwendeten Bezeichner an die matchenden Werte
gebunden werden und somit in der Funktion verwendet werden können.
Ausblick
Es gibt viele Dinge, die man mit Pattern Matching anstellen kann, und ich
möchte eure Entdeckerlust und Phantasie nicht unnötig einschränken. Probiert
einfach herum, stöbert im Web - anders haben wir das auch nicht gelernt. Ich
möchte trotzdem euren Blick auf einige Details lenken, die interessant sein
könnten:
• Was passiert, wenn ein Ausdruck auf mehrere der angegebenen Pattern
matchen kann?
• Was passiert, wenn mir Fälle fehlen?
• Funktioniert das auch mit selbst geschriebenen Datentypen?
Raphael Reitzig, 10. November 2007
Herunterladen