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.