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)