Scala pattern matching

pattern matching

The pattern matching in scala is similar to the switch syntax in Java, but Scala adds more functions from the syntax, so it is more powerful.

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) 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 throws MatchError.

(2) In each case, you do not 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 {}.

object TestPatternMatchBase {

    def main(args: Array[String]): Unit = {
        // 1. Basic definition syntax
        val x = 2
        val y = x match {
            case 1 => "one"
            case 2 => "two"
            case 3 => "three"
            case _ => "other"
        }
        println(y) // two

        // 2. Example: simple binary operation with pattern matching
        val a = 11
        val b = 99

        def matchDualOp(op: Char) = {
            op match {
                case '+' => a + b
                case '-' => a - b
                case '*' => a * b
                case '/' => a / b
                case '%' => a % b
                case  _ => -1
            }
        }

        println(matchDualOp('+')) // 110
        println(matchDualOp('-')) // -88
        println(matchDualOp('/')) // 0
        println(matchDualOp(')')) // -1
    }
}

Mode guard

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

object TestPatternMatchBase {

    def main(args: Array[String]): Unit = {
        // 3. Mode guard
        // Find the absolute value of an integer
        def abs(num: Int): Int = {
            num match {
                case i if i >= 0 => i
                case i if i < 0 => -i
            }
        }

        println(abs(-11)) // 11
        println(abs(13))  // 13
    }
}

Pattern matching type

Match constant

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

object TestMatchTypes {

    def main(args: Array[String]): Unit = {
        // Match constant
        def descConst(x: Any): String = {
            x match {
                case 1 => "Int one"
                case "hello" => "String hello"
                case true => "Boolean true"
                case '+' => "Char +"
                case _ => "other " + x
            }
        }
        println(descConst("hello")) // String hello
        println(descConst('+'))     // Char +
        println(descConst(0.3))     // other 0.3
    }
}

Match type

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

.

object TestMatchTypes {

    def main(args: Array[String]): Unit = {
        // Match type
        def descType(x: Any): String = {
            x match {
                case i: Int => "Int " + i
                case s: String => "String " + s
                case list: List[String] => "List " + list
                case array: Array[Int] => "Array[Int] " + array.mkString(",")
                case _ => "Something else " + x
            }
        }
        println(descType(35)) // Int 35
        println(descType("hello")) // String hello
        println(descType(List("hello", "word"))) // List List(hello, word)
        // Here, because the set will be "generic erased" during matching, and the Array does not directly belong to the set, but through implicit conversion, the Array can directly determine the type
        println(descType(List(1, 2))) // List List(1, 2)
        println(descType(Array("hi", "h"))) // Something else [Ljava.lang.String;@1cd072a9
        println(descType(Array(1, 2))) // Array[Int] 1,2
    }
}

Matching array

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

object TestMatchTypes {

    def main(args: Array[String]): Unit = {
        // Matching array
        for (arr <- List(
            Array(0), // Array(0)
            Array(1, 0), // Array(1, 0)
            Array(0, 1, 0), // Array starting with 0
            Array(1, 1, 0), // Three element array with 1 in the middle
            Array(2, 3, 5, 6), // other
            Array("hello", 1, 50) // Three element array with 1 in the middle
        )) {
            val result = arr match {
                case Array(0) => "Array(0)"
                case Array(1, 0) => "Array(1, 0)"
                case Array(x, y) => "Array: " + x + ", " + y // Match two element array
                case Array(0, _*) => "Array starting with 0"
                case Array(x, 1, y) => "Three element array with 1 in the middle"
                case _ => "other"
            }
            println(result)
        }
    }
}

Match list

object TestMatchTypes {

    def main(args: Array[String]): Unit = {
        // Match list
        // Mode 1
        for (list <- List(
            List(0), // List(0)
            List(1, 0), // List(x, y)
            List(0, 0, 0), // List(0, ...)
            List(1, 1, 0), // Something else
            List(88), // List(x)
            List("hello") // List(x)
        )) {
            val result = list match {
                case List(0) => "List(0)"
                case List(x, y) => "List(x, y)"
                case List(0, _*) => "List(0, ...)"
                case List(a) => "List(x)"
                case _ => "Something else"
            }
            println(result)
        }

        println("--------------------------")
        // Mode II
        val list1 = List(1, 2, 5, 7, 24)
        val list2 = List(1)

        def descListSecond(list: List[Any]): Unit = {
            list match {
                case first :: second :: rest => println(s"first: $first, second: $second, rest: $rest")
                case _ => println("Something else")
            }
        }
        descListSecond(list1) // first: 1, second: 2, rest: List(5, 7, 24)
        descListSecond(list2) // Something else

    }
}

Matching tuple

object TestMatchTuple {

    def main(args: Array[String]): Unit = {

        // Matching tuple
        // 1. Basic matching
        for (tuple <- List(
            (0, 1), // (a, b)
            (0, 0), // (a, b)
            (0, 1, 0), // (a, 1, b)
            (0, 1, 1), // (a, 1, b)
            (1, 23, 56), // Something else
            ("hello", true, 0.5) // Something else
        )) {
            val str = tuple match {
                case (a, b) => "(a, b)"
                case (0, _) => "(0, _)"
                case (a, 1, b) => "(a, 1, b)"
                case _ => "Something else"
            }
            println(str)
        }

        println("=============================")

        // 2. Match on variable declaration
        val (x, y) = (10, "hello")
        println(s"x: $x, y: $y")  // x: 10, y: hello

        val List(first, second, _*) = List(23, 15, 9, 78)
        println(s"first: $first, second: $second") // first: 23, second: 15

        val fir :: sec :: rest = List(23, 15, 9, 78)
        println(s"first: $fir, second: $sec, rest: $rest") // first: 23, second: 15, rest: List(9, 78)

        println("=============================")

        // 3. Pattern matching in for derivation
        val list: List[(String, Int)] = List(("a", 12), ("b", 35), ("c", 27))

        // 3.1 original traversal mode
        for (elem <- list) {
            println(elem._1 + " " + elem._2)
//            a 12
//            b 35
//            c 27
        }
        println("-------------")

        // 3.2 directly define the elements of List as tuples and assign values to variables
        for ((word, count) <- list) {
            println(word + " " + count)
//            a 12
//            b 35
//            c 27
        }
        println("-------------")

        // 3.3 do not consider the variables in a certain position, only traverse key or value
        for ((word, _) <- list) {
            println(word)
//            a
//            b
//            c
        }
        println("-------------")

        // 3.4 you can specify what the value of a location must be
        for (("a", count) <- list) {
            println(count) // 12
        }
    }
}

Matching objects and sample classes

Match object

object TestMatchObject {

    def main(args: Array[String]): Unit = {
        val alice = new Student("alice", 18)
        val bobo = new Student("bobo", 18)

        // Match object instance content
        def matchHandle(student: Student): Unit = {
            val result = student match {
                case Student("alice", 18) => "alice 18"
                case _ => "else"
            }
            println(result)
        }

        matchHandle(alice) // alice 18
        matchHandle(bobo)  // else
    }
}

// Define class
class Student(val name: String, val age: Int)

// Define companion objects
object Student {

    def apply(name: String, age: Int): Student = new Student(name, age)

    // You must implement an unapply method to disassemble object properties
    def unapply(student: Student): Option[(String, Int)] = {
        if (student == null) {
            None
        } else {
            Some((student.name, student.age))
        }
    }
}

val alice = Student("alice", 18). When this statement is executed, it actually calls the apply method in the User companion object, so the corresponding object can be constructed without the new keyword.

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

If the unapply method (extractor) of the object in case returns Some and all properties are consistent, the matching is successful. If the properties are inconsistent, or if None is returned, 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]]

Sample class

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

1. 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.

2 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)

object TestMatchCaseClass {

    def main(args: Array[String]): Unit = {
        val alice = new Student("alice", 18)
        val bobo = new Student("bobo", 18)

        // Match object instance content
        def matchHandle(student: Student): Unit = {
            val result = student match {
                case Student("alice", 18) => "alice 18"
                case _ => "else"
            }
            println(result)
        }

        matchHandle(alice) // alice 18
        matchHandle(bobo)  // else
    }
}

// Define sample classes
case class Student1(name: String, age: Int)

Pattern matching in partial function

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.

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

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)
    }
}

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 shown below

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 is executed; otherwise, the defaultut method is executed. The default method is the processing logic that the parameters do not meet the requirements.

Keywords: Scala

Added by superman on Mon, 10 Jan 2022 23:30:01 +0200