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)