Tuesday 26 December 2017

Introduction to Scala - XI

We continue with more Scala on the topic of Pattern Matching in this post. For all the work in this post, we will use Read-Evaluate-Print-Loop Scala interpreter. Some familiarity with Java will be of great help in understanding Scala. In this post, we will look at the different ways Pattern Matching can be implemented in Scala.

We already saw a brief example in an earlier post that is replicated below:

val x = 1
x match {
  case 1 => "One"
  case 2 => "Two" 
  case _ => "Unknown"
}

The results are shown below:


scala> val x = 1
x: Int = 1

scala> x match {
     |
     |   case 1 => "One"
     |
     |   case 2 => "Two"
     |
     |   case _ => "Unknown"
     | }
res104: String = One


We can also match on the basis of types as shown below:

def TypeMatch(input: Any) = input match {
  case i: Int => i.toBinaryString
  case d: Double => d.toInt
}

TypeMatch(-5)
TypeMatch(-5.9)


The results are shown below:

scala> def TypeMatch(input: Any) = input match {
     |   case i: Int => i.toBinaryString
     |   case d: Double => d.toInt
     | }
TypeMatch: (input: Any)Any

scala> TypeMatch(-5)
res105: Any = 11111111111111111111111111111011

scala> TypeMatch(-5.9)
res106: Any = -5


In the above example, the input is any object. If input is an integer, then, the input is converted into a binary string. If it is a Double, it is converted to an integer. Note that the case expressions, i and d are small case letters of the types that they describe.

Similar to above example, we can check for case classes as well as shown below:

case class Ferrari(Name: String, Type: String)
case class Ford(Name: String, Gas: String)

def CaseClassMatch(input: Any) = input match {
  case Ferrari(name, _) => s"Ferrari called $name"
  case Ford(name, gas) => s"Ford called $name, runs on $gas"
}


To test above code, we can use below code:

CaseClassMatch(Ferrari("California T","Automatic"))
CaseClassMatch(Ford("Endeavour","Diesel"))


The results are shown below:

 scala> case class Ferrari(Name: String, Type: String)
defined class Ferrari

scala> case class Ford(Name: String, Gas: String)
defined class Ford


 scala> def CaseClassMatch(input: Any) = input match {
     |   case Ferrari(name, _) => s"Ferrari called $name"
     |   case Ford(name, gas) => s"Ford called $name, runs on $gas"
     | }
CaseClassMatch: (input: Any)String


scala> CaseClassMatch(Ferrari("California T","Automatic"))
res120: String = Ferrari called California T

scala> CaseClassMatch(Ford("Endeavour","Diesel"))
res121: String = Ford called Endeavour, runs on Diesel


We can add a if clause to make this pattern matching more specific as shown in below program:

def CaseClassMatchWithIf(input: Any) = input match {
  case Ferrari(name, _) if (name == "California T") => s"Ferrari called $name"
  case Ford(name, gas) if (gas == "Petrol") => s"Ford called $name, runs on $gas"
}


Then, we can test the above code with below commands:

CaseClassMatchWithIf(Ferrari("CaliforniaT","Automatic")) 
CaseClassMatchWithIf(Ford("Endeavour","Petrol"))
CaseClassMatchWithIf(Ford("Endeavour","Diesel"))

The last command errors out as it fails the if test.

The results are shown below:

scala> def CaseClassMatchWithIf(input: Any) = input match {
     |   case Ferrari(name, _) if (name == "California T") => s"Ferrari called $name"
     |   case Ford(name, gas) if (gas == "Petrol") => s"Ford called $name, runs on $gas"
     | }
CaseClassMatchWithIf: (input: Any)String


scala> CaseClassMatchWithIf(Ferrari("California T","Automatic"))
res146: String = Ferrari called California T

scala> CaseClassMatchWithIf(Ford("Endeavour","Petrol"))
res147: String = Ford called Endeavour, runs on Petrol

scala> CaseClassMatchWithIf(Ford("Endeavour","Diesel"))
scala.MatchError: Ford(Endeavour,Diesel) (of class Ford)
  at .CaseClassMatchWithIf(<console>:16)
  ... 32 elided


This concludes the discussion on Pattern Matching in Scala.