Scala pattern matching

Chapter 1 pattern matching

Pattern matching in Scala is similar to the switch syntax in Java

int i = 10
switch (i) {
    case 10 :
        System.out.println("10");
        break;
    case 20 :
        System.out.println("20");
        break;
    default :
        System.out.println("other number");
        break;
}

But scala is more powerful because it adds more functions from the syntax.

1.1 basic grammar

In the pattern matching syntax, the match keyword is used to declare, and each branch is declared with the case keyword. When matching is needed, it will start from the first case branch. If the matching is successful, the corresponding logic code will be executed. If the matching is unsuccessful, continue to execute the next branch for judgment. If all cases do not match, the case is executed_ Branch, similar to the default statement in Java

object TestMatchCase {
  def main(args: Array[String]): Unit = {
    var a: Int = 10
    var b: Int = 20
    var operator: Char = 'd'

    var result = operator match {
      case '+' => a + b
      case '-' => a - b
      case '*' => a * b
      case '/' => a / b
      case _ => "illegal"
    }

    println(result)
  }
}

1. Description

(1) If all cases do not match, the case is executed_ Branch, similar to the default statement in Java, if there is no case at this time_ Branch, then throw MatchError

(2) In each case, there is no need to use the break statement to automatically interrupt the case

(3) The match case statement can match any type, not just literal

(4) = > the following code blocks until the next case statement are executed as a whole, which can be enclosed or not enclosed with {}

1.2 mode guard

1. Description

If you want to express data that matches a certain range, you need to add condition guard in pattern matching

2. Case practice

object TestMatchGuard {
  def main(args: Array[String]): Unit = {
    def abs(x: Int) = x match {
      case i: Int if i >= 0 => i
      case j: Int if j < 0 => -j
      case _ => "type illegal"
    }

    println(abs(-5))
  }
}

Output result:
5

1.3 pattern matching type

Match constant

1. Description

In Scala, pattern matching can match all literal quantities, including strings, characters, numbers, Boolean values and so on

2. Case practice

object TestMatchVal {
  def main(args: Array[String]): Unit = {
    println(describe(6))
  }

  def describe(x: Any) = x match {
    case 5 => "Int five"
    case "hello" => "String hello"
    case true => "Boolean true"
    case '+' => "Char +"
  }
}

Matching type

1. Description

When type judgment is required, you can use isInstanceOf[T] and asInstanceOf[T] learned above, or use pattern matching to achieve the same function

2. Case practice

object TestMatchClass {
  def describe(x: Any) = x match {
    case i: Int => "Int"
    case s: String => "String hello"
    case m: List[_] => "List"
    case c: Array[Int] => "Array[Int]"
    case someThing => "something else " + someThing
  }

  def main(args: Array[String]): Unit = {
    //Generic erase
    println(describe(List(1, 2, 3, 4, 5)))

    //Array exception, generic type can be reserved
    println(describe(Array(1, 2, 3, 4, 5, 6)))
    println(describe(Array("abc")))
  }
}

Output result:
List
Array[Int]
something else [Ljava.lang.String;@75222b

Matching array

1. Description

scala pattern matching can accurately match collections, such as an array with only two elements and the first element is 0

2. Case practice

object TestMatchArray {
  def main(args: Array[String]): Unit = {
    //Traverse an array collection
    for (arr <- Array(Array(0), Array(1, 0), Array(0, 1, 0), Array(1, 1, 0), Array(1, 1, 0, 1), Array("hello", 90))) {
      val result = arr match {
        //matching Array(0) This array
        case Array(0) => "0"
        //Match an array with two elements, and then assign the element value to the corresponding array x,y
        case Array(x, y) => x + "," + y
        //Match arrays starting with 0
        case Array(0, _*) => "Array starting with 0"

        case _ => "something else"
      }
      println("result = " + result)
    }
  }
}

Output results

result = 0
result = 1,0
result = array starting with 0
result = something else
result = something else
result = hello,90

Match list

1. Mode I

object TestMatchList  {
    def main(args: Array[String]): Unit = {
        //list It's a store List Array of collections
        // Think about it if you want to match List(88) Such a list contains only one element,And return the original value.How should I write it
        for (list <- Array(List(0), List(1, 0), List(0, 0, 0), List(1, 0, 0), List(88))) {
            val result = list match {
                case List(0) => "0"
                //matching List(0)
                case List(x, y) => x + "," + y
                //Matches two elements List
                case List(0, _*) => "0 ..."
                case _ => "something else"
            }
            println(result)
        }
    }
}

Output results

0
1,0
0 ...
something else
something else


2. Mode II

object TestMatchList {
    def main(args: Array[String]): Unit = {
        val list: List[Int] = List(1, 2, 5, 6, 7)
        list match {
            case first :: second :: rest => println(first + "-" + second + "-" + rest)
            case _ => println("something else")
        }
    }
}

Output results

1-2-List(5, 6, 7)

Matching tuple

object TestMatchTuple {
    def main(args: Array[String]): Unit = {
        //Traverse a tuple set
        for (tuple <- Array((0, 1), (1, 0), (1, 1), (1, 0, 2))) {
            val result = tuple match {
                //Is a tuple whose first element is 0
                case (0, _) => "0 ..."
                //The last element after matching is the dual tuple of 0
                case (y, 0) => "" + y + "0"
                case (a, b) => "" + a + " " + b
                //default
                case _ => "something else"
            }
            println(result)
        }
    }
}

Output results

0 ...
10
1 1

Extended case

object TestGeneric {
    def main(args: Array[String]): Unit = {
        //Special pattern matching 1 prints the first element of the tuple
        for (elem <- Array(("a", 1), ("b", 2), ("c", 3))) {
            println(elem._1)
        }
        for ((word, count) <- Array(("a", 1), ("b", 2), ("c", 3))) {
            println(word)
        }
        for ((word, _) <- Array(("a", 1), ("b", 2), ("c", 3))) {
            println(word)
        }
        for (("a", count) <- Array(("a", 1), ("b", 2), ("c", 3))) {
            println(count)
        }
        println("--------------")
        //Special pattern matching 2 names tuple elements
        var (id, name, age): (Int, String, Int) = (100, "zs", 20)
        println((id, name, age))
        println("--------------")
        //Special pattern matching 3 traverses the tuples in the set and gives count * 2
        var list: List[(String, Int)] = List(("a", 1), ("b", 2), ("c", 3))
        //println(list.map(t => (t._1, t._2 * 2)))
        println(
            list.map {
                case (word, count) => (word, count * 2)
            }
        )
        var list1 = List(("a", ("a", 1)), ("b", ("b", 2)), ("c", ("c", 3)))
        println(
            list1.map {
                case (groupkey, (word, count)) => (word, count * 2)
            }
        )
    }
}

Output result:

 

Matching objects and sample classes

1. Basic grammar

class User(val name: String, val age: Int)

object User {
    def apply(name: String, age: Int): User = new User(name, age)
    def unapply(user: User): Option[(String, Int)] = {
        if (user == null)
            None
        else
            Some(user.name, user.age)
    }
}
object TestMatchUnapply {
    def main(args: Array[String]): Unit = {
        val user: User = User("zhangsan", 11)
        val result = user match {
            case User("zhangsan", 11) => "yes"
            case _ => "no"    }
        println(result)
    }
}

2. Summary

val user = User("zhangsan", 11). When this statement is executed, it actually calls the apply method in the User associated object, so the corresponding object can be constructed without the new keyword.

When User("zhangsan", 11) is written after case [case User("zhangsan", 11) = > "yes"], the unapply method (object extractor) will be called by default. User is used as the parameter of the unapply method. The unapply method extracts the name and age attributes of the user object and matches the attribute values in User("zhangsan", 11)

If the unapply method (extractor) of the object in case returns Some and all attributes are consistent, the matching is successful. If the attributes are inconsistent or return None, the matching fails.

If only one attribute of the object is extracted, the extractor is unapply(obj:Obj):Option[T]

If multiple attributes of an object are extracted, the extractor is unapply(obj:Obj):Option[(T1,T2,T3...)]

If variable attributes of an object are extracted, the extractor is unapplySeq(obj:Obj):Option[Seq[T]]

3. Sample class

(1) Grammar

case class Person (name: String, age: Int)


(2) Explain

The sample class is still a class. Compared with ordinary classes, it only automatically generates associated objects, and some common methods are automatically provided in the associated objects, such as apply, unapply, toString, equals, hashCode and copy

The sample class is optimized for pattern matching because it provides the unapply method by default. Therefore, the sample class can directly use pattern matching without implementing the unapply method itself

Every parameter in the constructor becomes a val unless it is explicitly declared as var (this is not recommended)

(3) Case practice

Using the sample class in the case of the above matching object will save a lot of code

case class User(name: String, age: Int)
object TestMatchUnapply {
    def main(args: Array[String]): Unit = {
        val user: User = User("zhangsan", 11)
        val result = user match {
            case User("zhangsan", 11) => "yes"
            case _ => "no"
        }
        println(result)
    }
}

1.4 pattern matching in variable declaration

case class Person(name: String, age: Int)
object TestMatchVariable {
    def main(args: Array[String]): Unit = {
        val (x, y) = (1, 2)
        println(s"x=$x,y=$y")
        val Array(first, second, _*) = Array(1, 7, 2, 9)
        println(s"first=$first,second=$second")
        val Person(name, age) = Person("zhangsan", 16)
        println(s"name=$name,age=$age")
    }
}

Output results

x=1,y=2
first=1,second=7
name=zhangsan,age=16

1.5 pattern matching in for expression

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object TestMatchFor {
    
    def main(args: Array[String]): Unit = {
        val map = Map("A" -> 1, "B" -> 0, "C" -> 3)
        for ((k, v) <- map) { //Directly map Medium k-v Traverse out
            println(k + " -> " + v)
            //3 individual
        }
        println("----------------------")
        //ergodic value=0 of k-v ,If v Not 0,filter
        for ((k, 0) <- map) {
            println(k + " --> " + 0) // B->0
        }
        println("----------------------")

        //if v == 0 Is a filter condition
        for ((k, v) <- map if v >= 1) {
            println(k + " ---> " + v) // A->1 and c->33
        }
    }
}

Output result:

1.6 pattern matching in partial function (understanding)

Partial function is also a kind of function. Through partial function, we can easily check the input parameters more accurately.

For example, the input type of the partial function is List[Int], and what we need is a set whose first element is 0, which is realized through pattern matching.

Partial function definition

val second: PartialFunction[List[Int], Option[Int]] = {
case x :: y :: _ => Some(y)
}

Note: the function of this partial function is to return the second element of the input List set

Partial function principle

The above code will be translated into the following code by the scala compiler. Compared with ordinary functions, there is only one more function for parameter checking - isDefinedAt, whose return value type is Boolean

val second = new PartialFunction[List[Int], Option[Int]] {
    //Check whether the input parameters are qualified
    override def isDefinedAt(list: List[Int]): Boolean = list match {
    case x :: y :: _ => true
    case _ => false
    }

    //Execute function logic
    override def apply(list: List[Int]): Option[Int] = list match {
    case x :: y :: _ => Some(y)
    }
}

Partial function use

Partial functions cannot be used directly like second(List(1,2,3)), because this will directly call the apply method, but should call the applyOrElse method, as follows

second.applyOrElse(List(1,2,3), (_: List[Int]) => None)

The logic of the applyOrElse method is if (ifDefinedAt(list)) apply(list)else default. If the input parameters meet the conditions, that is, isDefinedAt returns true, the apply method will be executed; otherwise, the defaultut method will be executed. The default method is the processing logic that the parameters do not meet the requirements

Case practice

(1) Demand

Add one element of Int type in the List(1,2,3,4,5,6, "test") and remove the string

    def main (args: Array[String] ): Unit = {
        val list = List (1, 2, 3, 4, 5, 6, "test")
        val list1 = list.map {
            a =>
                a match {
                    case i: Int => i + 1
                    case s: String => s + 1
                }
        }
        println (list1.filter (a => a.isInstanceOf[Int] ) )
    }

(2) Practical operation

Method 1:

List(1,2,3,4,5,6,"test").filter(_.isInstanceOf[Int]).map(_.asInstanceOf[Int] + 1).foreach(println)

Method 2:

List(1, 2, 3, 4, 5, 6, "test").collect { case x: Int => x + 1 }.foreach(println)

Keywords: Scala

Added by titangf on Sat, 05 Feb 2022 14:30:03 +0200