Transcript Patterns

Pattern Matching
The match statement

C and Java have a switch statement which uses a small integer
value to choose among alternatives



In Java 7, it is now possible to use switch with Strings
switch is rarely used in Java programs
The Scala “equivalent” is the match expression

scala> val n = 4
n: Int = 4
scala> n match {
| case 1 => "one"
| case 2 => "two"
| case _ => "many"
| }
res0: String = many
2
Matching on values







scala> object Obj
defined module Obj
scala> val d = 5.0
d: Double = 5.0
scala> val lst = List(1, 2, 3)
lst: List[Int] = List(1, 2, 3)
scala> def valMatch(x: Any) = x match {
| case 5 => println("Int")
| case 5.0 => println("Double")
| case "abc" => println("String")
| case List(1, 2, 3) => println("List")
| case Obj => println("User-defined Obj")
| case _ => println("None of the above")
| }
valMatch: (x: Any)Unit
scala> valMatch(5)
Int
scala> valMatch(Obj)
User-defined Obj
scala> valMatch("abc")
String
3
Matching on types

scala> def whatKind(x: Any) = x match {
| case s: String => s"This is the string [$s]"
| case d: Double => s"$d is a Double"
| case i: Int => s"$i is an Int"
| case x => s"I don't know what $x is"
| }
whatKind: (x: Any)String
scala> whatKind(5.0)
res3: String = 5.0 is a Double
scala> whatKind(List(1, 2, 3))
res4: String = I don't know what List(1, 2, 3) is
4
Matching with guards

scala> def oddOrEven(n: Any) = n match {
|
case n: Int if n % 2 == 0 => "Even integer"
|
case n: Int => "Odd integer"
|
case x => x + " is something else"
| }
oddOrEven: (n: Any)String
scala> oddOrEven(5)
res18: String = Odd integer
scala> oddOrEven(6)
res19: String = Even integer
scala> oddOrEven(7.0)
res20: String = 7.0 is something else
5
Matching on user-defined types

For user-defined classes, you can match on the type




scala> class Person(val name: String)
defined class Person
scala> val dave = new Person("Dave")
dave: Person = Person@234bb715
scala> dave match {
| case n: Person => "ok"
| case _ => "nope!"
| }
res2: String = ok
But you can’t match on the particular object of a plain (non-case) class

scala> dave match {
| case Person("Dave") => "ok"
| case _ => "nope!"
| }
<console>:14: error: not found: value Person
6
Matching on objects of case classes

scala> val dave = new Person("Dave")
dave: Person = Person(Dave)

scala> val beth = new Person("Beth")
beth: Person = Person(Beth)

scala>
|
|
|
sayHi:

scala> sayHi(dave)
res7: String = Oh, it's just Dave.

scala> sayHi(beth)
res8: String = Hi, Beth!
def sayHi(p: Person) = p match {
case Person("Dave") => "Oh, it's just Dave."
case Person(name) => s"Hi, $name!"
}
(p: Person)String
7
Matching on exceptions

Scala’s try-catch-finally is similar to Java’s, but the
catch clauses are case expressions

try {
val f = new FileReader("input.txt")
} catch {
case ex: FileNotFoundException => {
println("Missing file exception")
}
case ex: IOException => {
println("IO Exception")
}
} finally {
println("Exiting finally...")
}

Source: http://www.tutorialspoint.com/scala/scala_exception_handling.htm
8
Matching on optional values


The Option type is used frequently in Scala
Scala’s None is used in places where Java might use null

scala> val scores = List(78, 43, 82, 67, 55)
scores: List[Int] = List(78, 43, 82, 67, 55)

scala> scores find (_ > 90)
res0: Option[Int] = None

scala> scores find (_ < 70)
res1: Option[Int] = Some(43)

scala> (scores find (_ < 30)) match {
| case None => "Everyone is passing"
| case Some(n) => n + " isn't very good"
| }
res2: String = Everyone is passing
9
Matching in assignments

scala> val jean = new Person("Jean", 23)
jean: Person = Person(Jean,23)
scala> jean match {
| case Person(n, a) => s"$n is $a years old"
| }
res5: String = Jean is 23 years old
scala> val Person(n, a) = jean
n: String = Jean
a: Int = 23
scala> val list = List(1, 2, 3, 4, 5)
list: List[Int] = List(1, 2, 3, 4, 5)
scala> val h :: hh :: t = list
h: Int = 1
hh: Int = 2
t: List[Int] = List(3, 4, 5)
10
Matching in for expressions

scala> val capitals = Map("France" -> "Paris", "Japan" ->
"Tokyo")
capitals: scala.collection.immutable.Map[String,String] =
Map(France -> Paris, Japan -> Tokyo)
scala> for ((country, city) <- capitals) {
|
println(s"The capital of $country is $city.")
| }
The capital of France is Paris.
The capital of Japan is Tokyo.

Values that don’t match are simply filtered out


scala> val scores = List(("John", 90), ("Mary", 100), ("Bill",
95), ("Jane", 100))
scores: List[(String, Int)] = List((John,90), (Mary,100),
(Bill,95), (Jane,100))
scala> for ((name, 100) <- scores) println(name)
Mary
Jane
11
Patterns as partial functions

A sequence of cases is a partial function, and may be used
anywhere a function literal may be used

scala> val factorial: Int => Int = {
| case 0 => 1
| case n => n * factorial(n - 1)
| }
factorial: Int => Int = <function1>=
scala> factorial(5)
res14: Int = 120

scala>
|
|
|
res16:
1, 10,
(1 to 10) map {
case n if n % 2 == 0 => n / 2
case n => 3 * n + 1
}
scala.collection.immutable.IndexedSeq[Int] = Vector(4,
2, 16, 3, 22, 4, 28, 5)
12
Failing to match


It’s an error if no case in a pattern match is satisfied
There are two basic choices for the last case:


case _ will match anything, and you don’t care what
case variable will match anything, and you can use the value
of the variable in the body of the case
13
isDefinedAt


A Map is a partial function from keys to values
The method isDefinedAt determines whether a partial
function is defined for a particular input value

scala> val scores = List(("John", 90), ("Mary", 100),
("Bill", 95), ("Jane", 100))
scores: List[(String, Int)] = List((John,90), (Mary,100),
(Bill,95), (Jane,100))

scala> val mapScores = scores.toMap
mapScores: scala.collection.immutable.Map[String,Int] =
Map(John -> 90, Mary -> 100, Bill -> 95, Jane -> 100)

scala> mapScores.isDefinedAt("William")
res1: Boolean = false

scala> mapScores.isDefinedAt("Bill")
res2: Boolean = true
14
collect

collect is a partial function that takes an iterator and returns a new iterator
over only defined values

def collect[B](pf: PartialFunction[A, B]): Iterator[B]

scala> val scores = Map(("John", 90), ("Mary", 100),
("Bill", 95), ("Jane", 100))
scores: scala.collection.immutable.Map[String,Int] =
Map(John -> 90, Mary -> 100, Bill -> 95, Jane -> 100)

scala> val names = List("John", "Frank", "Jane", "Joe")
names: List[String] = List(John, Frank, Jane, Joe)

scala> names collect scores
res10: List[Int] = List(90, 100)
15
Sealed classes


A class is sealed if (1) it is declared with the keyword sealed,
and (2) all subclasses are declared on the same file
This allows Scala to check for missing cases



scala> sealed class Person // omitting responses to save space
scala> class Man extends Person
scala> class Woman extends Person
scala> class Child extends Person
scala> val p: Person = new Woman
p: Person = Woman@12efc0ee
scala> p match {
|
case m: Man => "male"
|
case f: Woman => "female"
| }
<console>:12: warning: match may not be exhaustive.
It would fail on the following inputs: Child(), Person()
p match {
^
res5: String = female
16
@ and _*



In pattern matching, val @ pattern_part captures the value
of the pattern part in the value
In matching a sequence, _* matches the remainder of the
sequence
scala> list match {
| case a @ List(b @ _, c @ _*) => s"After $b in $a comes $c"
| }
res7: String = After 1 in List(1, 2, 3, 4, 5) comes List(2, 3, 4, 5)
17
The End
18