Scala - Pattern Matching 1 Pattern Matching Pattern Matching wird genutzt, um Daten anhand ihrer Struktur zu verarbeiten. 1.1 Konstante Pattern • Jede Konstante kann für match benutzt werden. • Beim Verwenden einer vorher definierten val, muss der erste Buchstabe groß geschrieben werden. • Als Alternative kann der Name der vorher definierten Konstanten in Backticks gesetzt werden. val Pi = Math.Pi val e = Math.E expr match { case 1 => println("eins") case Pi => println(Pi) case ‘e‘ => println(e) case _ => println("etwas anders") } 18. Mai 2010 1.6 Type patterns • Bestimmen des Variablentyps. • Erlaubt das gleichzeitige Feststellen des Typs und das Casten einer Variable. def cast(x: Any) = x match { case i: Int => println("Integer: " + i) case s: String => println("String: " + s) case d: Double => println("Double: " + d) case other => println("Etwas anderes: " + other) } 1.7 Variablen binden • Weiterverwenden eines inneren Patterns durch das @Zeichen. case class OneString(a: String) case class TwoStrings(a: OneString, b: OneString) def expr(x: Any) = x match { case TwoStrings(OneString(a), e @ OneString(b)) => e case _ => } 1.2 Variable Pattern • Beginnt der Parameter eines case mit Kleinbuchstaben, wird dieser als variables Pattern interpretiert. • Ein variables Pattern wird dem übergebenen Wert zugewiesen (ähnlich wie das Wildcard _, aber das übergebene Objekt wird daran gebunden). def patternMatch(x: Any) = x match { case a => println(a) } 1.3 Konstruktor Pattern • Jede Klasse mit dem Schlüsselwort case kann als case-Klasse verwendet werden. • Der match findet auf den Konstruktor der Klasse statt. case class Person(name: String, age: Int) person match { case Person("Alice", 25) => println("Hi Alice!") case Person(name, age) => println(name + " : " + age) } 2 Patternguards • Ein Patternguard erlaubt es, über eine if-Bedingung zu prüfen, ob eine bestimmte Eigenschaft erfüllt ist. • Ein match findet nur statt, wenn der Patternguard true zurückgibt. person match { case i: Int if i > 0 => println("Wert ist größer 0") case i: Int => println("Wert ist kleiner oder gleich 0") } 3 Sealed Klassen • Alle case-Klassen werden in einer Datei erzeugt und sind von einer Abstrakten Basisklasse mit dem Schlüsselwort sealed abgeleitet, die sich auch in der selben Datei befindet. • Mit dem Schlüsselwort sealed wir sichergestellt, dass keine zusätzlichen case-Klassen erzeugt werden. sealed class BinaryTree case class Node(value: Int, l: BinaryTree, r: BinaryTree) extends BinaryTree case object Empty extends BinaryTree 4 Option Type 1.4 Pattern Sequenzen • Suche nach bestimmten Sequenzen. • Mit dem Pattern _* kann auf eine Sequenz von Elementen angesprochen werden. expr match { // 1. Element ist eine 0, genau 3 Elemente case List(0, _, _) => println("first") // 2. Element ist eine 4, 2 oder mehr Elemente case List(_, 4, _*) => println("second") // Beliebig viele Elemente oder leere Liste case List(_*) => println("third") } • null zurückliefern ist in Scala nicht immer möglich. • Als Alternative gibt es den Option Type, der entweder Some(x) oder None zurückliefert. • Zum Extrahieren des Wertes aus Some() bieten sich ein pattern match an. scala> def show(x: Option[String]) = x match { case Some(s) => s case None => "?" } 5 Pattern außerhalb von match Pattern sind in vielen Teilen von Scala erlaubt und nicht nur in match-Ausdrücken. 1.5 Tuple patterns • Auch Tuple können mit Hilfe eines Pattern Macht gefunden werden. def tuple(x: Any) = x match { case (a, b, c) => "Dreier Tupel: " + a + ", " + b + ", " + c case _ => } Dennis Wilfert 5.1 Pattern in Variablendefinitionen • Tuple lassen sich einfach in einzelne Variablen aufteilen. scala> val tuple = (123, "abc") tuple: (Int, java.lang.String) = (123,abc) scala> val (n, s) = tuple n: Int = 123 s: java.lang.String = abc 1 Scala - Pattern Matching 5.2 case-Sequenzen als partielle Funktionen • Eine Sequenz von case kann in jeder partiellen Funktion als Eintrittspunkt verwendet werden. • Eine case-Sequenz hat mehrere Eintrittspunkte, jede mit ihrer eigenen Liste von Parametern. 18. Mai 2010 7 Reguläre Ausdrücke (regular expressions) • Scala übernimmt die regular expression syntax aus Java. • Nach einem import scala.util.matching.Regex können reguläre Ausdrücke verwendet werden. val Words = new Regex("(\\w+)") val withDefault: Option[Int] => Int = { case Some(x) => x case None => 0 } • Zurückgeben eines bestimmten Objekts aus einer Liste. • PartialFunktion liefert Methoden um sicherzustellen, dass die Liste wirklich so viele Elemente besitzt. Ansonsten würde eine Compilerwarnung auftreten. • regex findFirstIn str findet erstes Auftreten des regulären Ausdrucks. • regex findAllIn str findet jedes Auftreten des regulären Ausdrucks. • regex findPrefixOf str. Der reguläre Ausdruck muss an erster Position auftreten. val input = "Einzelne Wörter eines Strings" val second: PartialFunction[List[Int],Int] = { case x :: y :: _ => y } for (s <- Words findAllIn input) println(s) second.isDefinedAt(List()) 7.1 Extrahieren mit regulären Ausdrücken • Jeder reguläre Ausdruck in Scala definiert einen Extractor. 5.3 Pattern in for-Ausdrücken • Verwenden von Pattern in for-Ausdrücken. val tupleList = List( ("Dennis", "Wilfert"), ("John", "Doe") ) for((a, b) <- tupleList) println("Vorname: " + a + " Nachname: " + b) Vorname: Dennis Nachname: Wilfert Vorname: John Nachname: Doe // Sucht alle Zahlen (negativ und positiv) in einem String val Decimal = """(-)?(\d+)(\.\d*)?""".r scala> val Decimal(sign, integerpart, decimalpart) = "-1.23" sign: String = integerpart: String = 1 decimalpart: String = .23 6 Extractoren 6.1 Extractoren • Extractoren erlauben es, ein Pattern zu erstellen ohne eine damit verbundene case-Klasse zu schreiben. • Ein Extractor ist ein object, dass eine Methode unapply besitzt. • Wird das Extractorobjekt für ein Patternmatch verwendet wird bei einem Match die Methode unapply aufgerufen. • Eine Methode apply ist oft auch vorhanden, aber nicht zwingend notwendig.. • Sind in einem Objekt apply und unapply definiert, sollte ein Aufruf unapply(apply(a, b)) Some(a, b) zurückliefern. object Twice { def apply(x: Int): Int = x * 2 def unapply(z: Int): Option[Int] = if (z%2 == 0) Some(z/2) else None } twice match { case Twice(n) => println(n) } 6.2 Extractoren mit variabler Argumentzahl • Für einen Extractor mit einer variablen Anzahl an Rückgabewerten, muss eine Methode unapplySeq implementiert werden. • Das Ergebnis von unapplySeq muss Option[Seq[T]] sein. def unapplySeq(s: String): Option[Seq[String]] = Some(s.split(" ")) } Dennis Wilfert 2