Big data technology AI
Flink/Spark/Hadoop / data warehouse, data analysis, interview, source code interpretation and other dry goods learning materials
101 original content
official account
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 functionality from the syntax.
1 basic grammar
-
In pattern matching syntax, match keyword is used to declare
-
Each branch is declared with the case keyword
-
When a match is needed, it starts with the first case branch
-
If the match is successful, the corresponding logical code is executed
-
If the match 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
val a = 25 val b = 13 def matchDualOp(op: Char): Int = op match { case '+' => a + b case '-' => a - b case '*' => a * b case '/' => a / b case '%' => a % b case _ => -1 } println(matchDualOp('+')) println(matchDualOp('/')) println(matchDualOp('\\'))
- explain:
(1) If all cases do not match, the case branch will be executed, which is 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 block, and the code until the next case statement is executed as a whole,
You can use {} to enclose or not.
2 mode guard
- explain
If you want to express data that matches a certain range, you need to add conditional guard in pattern matching.
- Case practice
def abs(num: Int): Int = { num match { case i if i >= 0 => i case i if i < 0 => -i } } println(abs(67))
3 pattern matching type
3.1 matching constants
def test(x: Any): String = x match { case 1 => "Int one" case "hello" => "String hello" case true => "Boolean true" case '+' => "Char +" case _ => "" } println(test("hello")) println(test('+')) println(test(0.3))
String hello Char +
3.2 matching 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.
def test(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(",") //Generic erase case m: List[_] => "List"+m case a => "else: " + a } println(test(35)) println(test("hello")) //Generic erase println(test(List("hi", "hello"))) //Generic erase println(test(List(2, 23))) println(test(Array("hi", "hello"))) println(test(Array(2, 23)))
Int 35 String hello List List(hi, hello) List List(2, 23) else: [Ljava.lang.String;@6e8dacdf Array[Int] 2,23
3.3 matching array
scala pattern matching can accurately match collections, such as matching the first element with only two elements
Array with 0.
for (arr <- List( Array(0), Array(1, 0), Array(0, 1, 0), Array(1, 1, 0), Array(2, 3, 7, 15), Array("hello", 1, 30) )) { val result = arr match { case Array(0) => "0" case Array(1, 0) => "Array(1, 0)" // Match two element array case Array(x, y) => "Array: " + x + ", " + y case Array(0, _*) => "Array starting with 0" case Array(x, 1, z) => "Three element array with 1 in the middle" case _ => "else" } println(result) }
0 Array(1, 0) Array starting with 0 Three element array with 1 in the middle else Three element array with 1 in the middle
3.4 matching list
Mode 1:
for (list <- List( List(0), List(1, 0), List(0, 0, 0), List(1, 1, 0), List(88), List("hello") )) { val result = list match { case List(0) => "0" case List(x, y) => "List(x, y): " + x + ", " + y case List(0, _*) => "List(0, ...)" case List(a) => "List(a): " + a case _ => "else" } println(result) }
0 List(x, y): 1, 0 List(0, ...) else List(a): 88 List(a): hello
Mode 2:
val list = List(1, 2, 5, 7, 24) //val list = List(24) list match { case first :: second :: rest => println(s"first: $first, second: $second, rest: $rest") case _ => println("else") }
first: 1, second: 2, rest: List(5, 7, 24)
3.5 matching tuples
for (tuple <- List( (0, 1), (0, 0), (0, 1, 0), (0, 1, 1), (1, 23, 56), ("hello", true, 0.5) )){ val result = tuple match { case (a, b) => "" + a + ", " + b //Is a tuple whose first element is 0 case (0, _) => "(0, _)" //Is a tuple whose second element is 1 case (a, 1, _) => "(a, 1, _) " + a case (x, y, z) => "(x, y, z) " + x + " " + y + " " + z case _ => "else" } println(result) }
a 12 b 35 c 27 a 13 a: 12 b: 35 c: 27 a: 13
3.6 matching objects
object test { def main(args: Array[String]): Unit = { val student = Student("Outlaw maniac, Zhang San", 18) // Match the contents of the object instance val result = student match { case Student("Outlaw maniac, Zhang San", 18) => "Outlaw maniac, Zhang San, 18" case _ => "Else" } println(result) } } // 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 student = Student("outlaw, Zhang San", 11). When this statement is executed, it actually calls the * * * * apply method in the Student's associated object, so the corresponding object can be constructed without the new keyword.
-
When Student("outlaw maniac, Zhang San, 11") is written after case [case Student("outlaw maniac, Zhang San, 11) = >" outlaw maniac, Zhang San, 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, Match the attribute value in Student("outlaw maniac, Zhang San", 11)
-
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]]
3.7 sample classes
object test { def main(args: Array[String]): Unit = { val user = User("zhangsan", 18) // Match the contents of the object instance val result = user match { case User("zhangsan", 18) => "zhangsan, 18" case _ => "Else" } println(result) } } // Define sample classes case class User(name: String, age: Int)
-
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)
View bytecode file:
User$:
package com.duo; import scala.None.; import scala.Option; import scala.Serializable; import scala.Some; import scala.Tuple2; import scala.runtime.AbstractFunction2; import scala.runtime.BoxesRunTime; public final class User$ extends AbstractFunction2<String, Object, User> implements Serializable { public static final MODULE$; static { new (); } public final String toString() { return "User"; } public User apply(String name, int age) { return new User(name, age); } public Option<Tuple2<String, Object>> unapply(User x$0) { return x$0 == null ? None..MODULE$ : new Some(new Tuple2(x$0.name(), BoxesRunTime.boxToInteger(x$0.age()))); } private Object readResolve() { return MODULE$; } private User$() { MODULE$ = this; } }
User:
import scala.Function1; import scala.Option; import scala.Product; import scala.Product.class; import scala.Serializable; import scala.Tuple2; import scala.collection.Iterator; import scala.reflect.ScalaSignature; import scala.runtime.BoxesRunTime; import scala.runtime.ScalaRunTime.; import scala.runtime.Statics; @ScalaSignature(bytes="\006\001\005....") public class User implements Product, Serializable { private final String name; private final int age; public static Option<Tuple2<String, Object>> unapply(User paramUser) { return User..MODULE$.unapply(paramUser); } public static User apply(String paramString, int paramInt) { return User..MODULE$.apply(paramString, paramInt); } public static Function1<Tuple2<String, Object>, User> tupled() { return User..MODULE$.tupled(); } public static Function1<String, Function1<Object, User>> curried() { return User..MODULE$.curried(); } public String name() { return this.name; } public int age() { return this.age; } public User copy(String name, int age) { return new User(name, age); } public String copy$default$1() { return name(); } public int copy$default$2() { return age(); } public String productPrefix() { return "User"; } public int productArity() { return 2; } public Object productElement(int x$1) { int i = x$1; switch(i) { default: throw new IndexOutOfBoundsException(BoxesRunTime.boxToInteger(x$1).toString()); case 1: break; case 0: } return name(); } public Iterator < Object > productIterator () { return ScalaRunTime..MODULE$.typedProductIterator(this); } public boolean canEqual(Object x$1) { return x$1 instanceof User; } public int hashCode() { int i = -889275714; i = Statics.mix(i, Statics.anyHash(name())); i = Statics.mix(i, age()); return Statics.finalizeHash(i, 2); } public String toString() { return ScalaRunTime..MODULE$._toString(this); } public boolean equals(Object x$1) { if (this != x$1) { Object localObject = x$1; int i; if ((localObject instanceof User)) i = 1; else i = 0; if (i == 0) break label96; User localUser = (User) x$1; str = localUser.name(); String tmp42_32 = name(); if (tmp42_32 == null) { tmp42_32; if (str == null) break label63; tmpTernaryOp = tmp42_32; break label88; } } } public User (String name, int age) { Product .class.$init$(this); } }
4 pattern matching in variable declaration
val (x, y) = (10, "hello") println(s"x: $x, y: $y") val List(first, second, _*) = List(23, 15, 9, 78) println(s"first: $first, second: $second") val fir :: sec :: rest = List(23, 15 , 9, 78) println(s"first: $fir, second: $sec, rest: $rest")
5. Pattern matching in the derivation
val list: List[(String, Int)] = List(("a", 12), ("b", 35), ("c", 27), ("a", 13)) // 1 original traversal mode for (elem <- list){ println(elem._1 + " " + elem._2) } // 2 directly define the elements of the List as tuples and assign values to variables for ((word, count) <- list ){ println(word + ": " + count) } // 3. You can traverse only key or value without considering the variables at a certain position for ((word, _) <- list) println(word) // 4 you can specify what the value of a location must be for (("a", count) <- list){ println(count) } // 5 cycle guard for ((k, v) <- map if v >= 1) { println(k + " ---> " + v) }
6 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 through the pattern
Matching implemented
(1) Partial function definition
val second: PartialFunction[List[Int], Option[Int]] = { case x :: y :: _ => Some(y) }
(2) 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 parameter
The function to be checked -- 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) } }
(3) Partial function use
Partial functions cannot be used directly like second(List(1,2,3)), because this will directly call the apply method, instead of
This calls 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 parameter is full
If the condition is sufficient, that is, isDefinedAt returns true, execute the apply method; otherwise, execute the defaultut method and the default method
Processing logic for parameters that do not meet the requirements.
(4) Case practice
Say the second element * 2 of tuple in list
val list: List[(String, Int)] = List(("a", 12), ("b", 35), ("c", 27), ("a", 13)) // 1. map conversion, realizing that the key remains unchanged and the value is 2 times val newList = list.map( tuple => (tuple._1, tuple._2 * 2) ) // 2. Use pattern matching to assign values to tuple elements to realize the function val newList2 = list.map( tuple => { tuple match { case (word, count) => (word, count * 2) } } ) // 3. Omit the writing method of lambda expression and simplify it val newList3 = list.map { case (word, count) => (word, count * 2) }