Objective
First of all, we want to emphasize one important thing at the outset: Scala's pattern matching occurs but is not limited to the match case
statement block, which is one of the key factors that Scala pattern matching is important and useful! We will discuss this in detail in the latter part of the article.
Types of pattern matching
There are several types of pattern matching in Scala:
Wildcard match (Wildcard Pattern Matching)
Constant Match (Constant Pattern Matching)
Variable matching (Variable Pattern Matching)
Constructor Match (Constructor Pattern Matching)
Collection type matching (Sequence Pattern Matching)
Ganso type Matching (Tuple Pattern Matching)
Type match (Typed Pattern Matching)
An all-encompassing example
Let's take a look at an example that shows almost all types of pattern matching:
Object patternmatchingdemo { Case class person(firstname:string, lastname:string) Case class Dog(name:string) defEchowhatyougaveme (x:any): String = xMatch{//constant Patterns Case 0="Zero" Case true="true" Case "Hello"="You said ' Hello '" CaseNil ="An empty List" //Sequence patterns CaseList (0, _, _) = ="A three-element list with 0 as the first element" CaseList (1, _*) ="A list beginning with 1, have any number of elements" CaseVector (1, _*) ="A vector starting with 1, have any number of elements" //Tuples Case(A, b) = + S"got $a and $b" Case(A, b, c) = + S"Got $a, $b, and $c" //Constructor Patterns CasePerson (first,"Alexander") = S"found an Alexander, first name = $first" CaseDog ("Suka") ="found a dog named Suka" //typed patterns CaseS:string = S"You gave me this string: $s" CaseI:int = S"Thanks for the int: $i" CaseF:float = S"Thanks for the float: $f" CaseA:array[int] = s"An array of int: ${a.mkstring (",")}" CaseAs:array[string] = s"An array of strings: ${as.mkstring (",")}" CaseD:dog = S"Dog: ${d.name}" CaseList:list[_] = s"Thanks for the List: $list" CaseM:map[_, _] = m.tostring//The default wildcard pattern Case_ ="Unknown"}defMain (args:array[string]) {//Trigger the constant patternsprintln (Echowhatyougaveme (0)) println (Echowhatyougaveme (true)) println (Echowhatyougaveme ("Hello")) println (Echowhatyougaveme (Nil))//trigger the sequence patternsprintln (Echowhatyougaveme (List (0,1,2)) println (Echowhatyougaveme (List (1,2)) println (Echowhatyougaveme (List (1,2,3))) println (Echowhatyougaveme (Vector (1,2,3)))//trigger the tuple patternsprintln (Echowhatyougaveme (1,2)))//Element tupleprintln (Echowhatyougaveme (1,2,3)))//three element tuple //Trigger the constructor patternsprintln (Echowhatyougaveme (Person ("Melissa","Alexander")) println (Echowhatyougaveme (Dog) ("Suka")))//trigger the typed patternsprintln (Echowhatyougaveme ("Hello, World")) println (Echowhatyougaveme ( the)) println (Echowhatyougaveme ( theF)) println (Echowhatyougaveme (Array (1,2,3))) println (Echowhatyougaveme (Array ("Coffee","Apple pie")) println (Echowhatyougaveme (Dog) ("Fido")) println (Echowhatyougaveme (List ("Apple","Banana")) println (Echowhatyougaveme (MAP) (1-"Al",2-"Alexander")))//Trigger the wildcard patternprintln (Echowhatyougaveme ("33d")) }}
The corresponding input is as follows:
zerotrueyou said ‘hello‘an empty Lista three-element list with 0 as the first elementa list beginning with 1, having any number of elementsa list beginning with 1, having any number of elementsa vector starting with 1, having any number of elementsgot 1 and 2got 1, 2, and 3found an Alexander, first name = Melissafound a dog named Sukayou gave me this string: Hello, worldthanks for the int: 42thanks for the float: 42.0an array of int: 1,2,3an array of strings: coffee,apple piedog: Fidothanks for the List: List(apple, banana)Map(1 -> Al, 2 -> Alexander)you gave me this string: 33d
The only thing that is not shown in the above example is variable pattern matching. Variable pattern matching is much like wildcard pattern matching, the only difference is that when you use wildcard pattern matching, you cannot use a matching value after the case derivation, but the variable pattern match gives the matching value a name for the variable, so you can use it after the derivation. The following example demonstrates variable pattern matching:
defmatch { case i:Int => s"This is an Integer: $i" case otherValue => s"This is other value: $otherValue"//You can use var: otherValue. | }variableMatch: (x: Any)Stringscala> println(variableMatch(11scala> println(variableMatch(1.01.0scala> println(variableMatch("SSS"))This is other value: SSS
Additional constraints for pattern matching (Guard)
The above 7 pattern matching is the syntactic level of pattern matching, many times, only these 7 pattern matching is not enough, the programmer needs to make a more detailed match according to the specific value, at this time, we need to add more constraints to the pattern matching, these constraints are called guard, The corresponding to code is to add an if statement after the case to make a more detailed description of the match. Let's look at an example:
def testPatternGuard(x: (Int,Int)):Int = x match { | case (a,a)=>a*2 | case (a,b)=>a+b | }<console>:8isas value a case (a,a)=>a*2 ^
The above code is designed to determine whether the two values in the two tuple are not the same by pattern matching, and if it is the same, use one of the computational logic, and if not, use another calculation logic, but this code is not compiled, Scala requires "mode must be linear ", which means that a variable in a pattern can only occur once. (Scala restricts patterns to is linear:a pattern variable may only appear once in a pattern.) I would like to use a variable in this example to let Scala help you at compile time. Judging whether two value is a value is obviously not done, so the inevitable error, in this case is required to use if statement to qualify the match condition, the following correct practice:
scala> def testpatternguard (x: (int,int) ) : String = x Match {| case (A, B ) if a==b =>s "A==b,so, we can calc it as:a*2=${a*2}"
| Case (A, b) =>s "A!=b,just Calc It as:a+b=${a+b}" | }testpatternguard: (x: (int, int)) stringscala> println (Testpatternguard (1 , Span class= "Hljs-number" >2 )) A!=b,just Calc It as : A+b=3 scala> println (Testpatternguard (1 , 2 ))) A!=b,just Calc It as : A+b=3
Sealed Classe and pattern matching
If a class is declared as sealed, a class is not allowed to inherit from the class except in the file in which it is defined that you can create its subclasses. In the case of pattern matching, we need to be mindful of whether your case statements can cover all possible situations, but if you match a class family, especially subclasses, there can be uncontrollable situations, because if the class family is freely downward derived, case statements that have covered various scenarios in the past may no longer "All in all." So using sealed class is a protection against pattern matching. In addition, the sealed class can be used to get some additional benefits from the compiler: When you try to match a case class that is inherited from the sealed class for a case, if you omit some cases class, the compiler gives a warning at compile time. So, when you want to create a class family for a pattern match, or if your class family is going to be used by GF for pattern matching, you'd better consider limiting your class-family superclass to sealed. For example, when you define such a set of sealed classes:
Sealed Abstract class Expr Case class Var(name:string) extends Expr Case class number(num:double) extends Expr Case class unop(operator:string, arg:expr) extends Expr Case class binop(operator:string,left:expr, right:expr) Extends Expr
If you write such a pattern match:
def describe(e: Expr): String = e match { "a number" "a variable" | }<console>:12not be exhaustive.It would fail on the following inputs: BinOp(_, _, _), UnOp(_, _) def describe(e: Expr): String = e match { ^describe: (e: Expr)String
The compiler will give you a warnining.
Pattern matching everywhere
All of the pattern matches we demonstrated above are based on match case
statement blocks, as we emphasized at the beginning of the article: if pattern matching exists only in the match case
statement, the energy of the radiation of this excellent feature will be compromised, and Scala will promote the pattern matching to all aspects of programming, To make pattern matching truly shine in Scala.
Pattern matching in a variable definition
This is probably the most fascinating way to get a Scala pattern match, and in Scala, you can assign values to multiple variables at once by using pattern matching whenever you define a variable! This feature is widely used to extract the corresponding values from tuples, case classes, and constructors to multiple variables. Several common examples are shown below:
Extracting variables from tuples
scala> val (number,string)=(1,"a")number1stringascala> println(s"number=$number")number=1scala> println(s"string=$string")string=a
Extracting the amount variable from the constructor
case class Person(name:String,age:Int)class Personval Person(name,age)=Person("John",3030 scala> println(s"name=$name")name=Johnscala> println(s"age=$age")age=30
A more common example is extracting a list of arguments passed by the command line in the main function:
def main(args: Array[String]) { val Array(arg1,agr2)=args .....}
Pattern matching in case statement block (function number plane amount)
In the Scala partial function partial function article we describe in detail the partial functions, which refer to the use of a case block without match to construct the literal of a partial function, which has multiple "portals", each of which is described by a case, This way, when a partial function is called, it is matched to a case based on the parameters passed in, which is also a pattern match, which is similar to the pattern match for match.
Pattern matching in a for loop
If we think that the local iteration variable declared in the For loop is a normal variable, then the pattern match used in the For loop is essentially the pattern match used in the previously mentioned variable definition, to see an argument:
Map("France""Paris""Japan""Tokyo")capitals: scala.collection.immutable.Map[String,StringMapfor ((country, city) <- capitals) | println("The capital of "+ country +" is "ofisofis Tokyo
A deeper understanding of why we need "pattern matching"?
In an interview with Martin Odersky, Martin Odersky explains:
Each of us has complex data. If we stick to the strict object-oriented style, then we don't want direct access to the tree structure within the data. Instead, we want to invoke the method and then access it in the method. If we can do this, then we don't need pattern matching anymore because these methods already provide the functionality we need. But in many cases, objects do not provide the methods we need, and we cannot (or do not) add methods to those objects. ..... In essence, pattern matching is essential when you get an object graph with structure from the outside. You will encounter this phenomenon in a number of situations.
In this discussion, and Martin Odersky's example of building a DOM type structure from XML, I'm reminded of the article I wrote earlier: a meditation on "polymorphism," in which the question I'm thinking about is the perfect scenario for applying pattern matching!
Apply pattern matching to fetch a bunch of values, so good, why not?
It's like some kind of inverse expression. A forward expression inserts a value into the result, and the inverse expression is the given result, and once the match succeeds, it can then extract a large pile of values from the result.
Scala pattern matching (Patterns Matching)