14b. Pattern Matching - RobertMakyla/scalaWiki GitHub Wiki
KEY POINTS: The match expression is a better switch, without fall-through (no need to use 'break' to exit switch after each branch). If no pattern matches, a MatchError is thrown. Use the case _ pattern to avoid that. A pattern can include an arbitrary condition, called a guard. You can match on the type of an expression; prefer this over isInstanceOf/asInstanceOf. You can match patterns of arrays, tuples, and case classes, and bind parts of the pattern to variables. In a for expression, nonmatches are silently skipped.
A Better Switch
Used as statement
val ch:Char = '+'
myChar match {
case '+' => print("positive")
case '-' => print("negative")
case _ => print("N/A") // default case should be there,
} // cause if not, and no case match, we get MatchError
Used as expression
val ch:Char = '+'
val mySign = ch match {
case '+' => 1
case '-' => -1
case _ => 0
}
I can match not only number but any types.
Guards - for a group of values to be treated with one action
ch match {
case '+' => sign = 1
case '-' => sign = -1
case _ if Character.isDigit(ch) => digit = Character.digit(ch, 10)
case _ => sign = 0
}
Variables in Patterns
ch match {
case '+' => sign = 1
case '-' => sign = -1
case someChar => digit = Character.digit(ch, 10) // variable as matcher
case otherChar if otherChar.length > 0 => digit = Character.digit(ch, 10) // also with guard
}
Unattended test Note : mapowanie Stringów ("multiply by" -> funkcja) będzie musiało być w takim match/case na Stringach a ostatni case rzuca exception
Constant patterns - concrete values
def describe(x: Any) = x match {
case 5 => "five"
case true => "truth"
case "hello" => "hi!"
case Nil => "the empty list"
case _ => "something else"
}
Constant patterns - constants
case Pi => ... // I can use (scala.math) constant only if the start with UpperCase
case `pathSeparator` => // if constant starts with lowCase I must enclose it in backquotes
// pathSeparator comes from java.io.File
Type Patterns (much preferred than using instanceOf )
obj match {
case x: Int => x // in case obj is Int
case s: String => s.length // in case obj is String
case m: Map[_,_] => m.size // in case obj is map
case _ => 0 // in other case (_ when we don't need to bind what we matched)
}
Caution:
case _: String => ... // Matches any object of type String
case BigInt => ... // Matches the String object of type Class
case m: Map[String, Int] => ... // Error. I cannot match like that cause **generic types are erased in JVM**
// and matching occurs during runtime
case m: Array[Int] => ... // OK - that's an exception, Arrays are not erased.
case m: Map[_, _] => ... // OK
Matching Arrays, Lists, and Tuples
arr match {
case Array(0) => "0" // matches if Array has 1 element: 0
case Array(x, y) => x + " " + y // matches if Array has 2 elements (and binds them to x and y)
case Array(0, _*) => "0 ..." // matches if Array starts with zero
case _ => "something else"
}
The same matches for list and tuples:
lst match {
case 0 :: Nil => "0"
case x :: y :: Nil => x + " " + y
case 0 :: tail => "0 ..."
case _ => "something else"
}
pair match {
case (0, _) => "0 ..."
case (y, 0) => y + " 0"
case _ => "neither is 0"
}
Destructing - is getting access to elements from structure of collections during matching
Extractors - making matching arrays/lists/tuples possible
Extractors have apply/unapply methods to extract values
For 'case Array(0, x) => ...' the Array companion object is an extractor - it defines an unapplySeq method
It calls Array.unapplySeq(arr), where arr is (0,x)
Matching RegExp
val pattern = "([0-9]+) ([a-z]+)".r
"99 bottles" match {
case pattern(num, item) => println("regexp: ("+num+") ("+item+")")
// Sets num to "99", item to "bottles"
}
pattern.unapplySeq("99 bottles") yields a sequence of strings that match the groups.
They are assigned to num/item.
Here extractor isn't the companion object but expression object.
Patterns in Variable Declarations
val (x, y) = (1, 2) // defines x as 1 and y as 2
val Array(first, second, _*) = arr // assigns first and second element of the array arr
// to the variables first and second.
Patterns in 'for' Expressions
import scala.collection.JavaConversions.propertiesAsScalaMap
// Converts Java Properties to a Scala map—just to get an interesting example
for ((k, v) <- System.getProperties())
println(k + " -> " + v)
for ((k, "") <- System.getProperties()) //taking only this keys with empty values
println(k)
it' the same as using guard:
for ((k, v) <- System.getProperties() if v == "")
println(k)