Scala pattern matching learning Notes

Source: Internet
Author: User
Tags abstract case statement constructor


Pattern matching may be introduced through functional programming and is widely used in programming language functions, rather than the previously used regular expression, it is only used for character string processing. Before that, let's take a look at the pattern matching in Haskell. I have mentioned this factorial example here:


Factorial: (Integral a) => a->
Factorial 0 = 1
Factorial n = n * factorial (n-1)

There is no extra explanation at all, so you can understand it at a glance. Pattern matching plays the role of if-else and plays a "change point" role in logical execution. In traditional static languages, we need to add "change points" to programs ", either the if-else statement (in essence, the switch-case Statement and the Map statement are used to find the matching value also belongs to the if-else statement), or is a polymorphism or method overload. Now we see a new weapon for modifying the program's logical steps based on parameters. Although this example does not seem to be much different from if-else, the advantages of this method are shown when different parameter combinations exist:


Translate: String-> String
Translate ('$': x) = "Dollar:" ++ x
Translate (_: x) = "Unknown:" ++ x

The Underscore "_" is a wildcard. In this way, pattern is similar to the switch-case with the default statement. The last wildcard ensures that no exception is thrown and all cases are covered.

Then move it to Scala to see the pattern matching, which is also supported in the above cases. Pattern matching does not necessarily apply only to a single parameter as a whole for matching. Parameters can also be split, for example:

1
List (1, 2, 3) match {case List (_, _, 3) => println ("OK ")}
This ignores the first two parameters and directly compares whether the third parameter is 3. Of course, in addition to the above situation, pattern matching can also match the type of the parameter.

It not only applies to the parameter level, but also to the class and object level. For example, the example on the homepage of the Scala official website is as follows:


// Define a set of case classes for representing binary trees.
Sealed abstract class Tree
Case class Node (elem: Int, left: Tree, right: Tree) extends Tree
Case object Leaf extends Tree
// Return the in-order traversal sequence of a given tree.
Def inOrder (t: Tree): List [Int] = t match {
Case Node (e, l, r) => inOrder (l): List (e): inOrder (r)
Case Leaf => List ()
}

Tree itself can be implemented in two types. One is Node, which is a class and accepts its own values, the left subtree, and the right subtree; the other is Leaf, which is a Leaf instance (not a class ). When implementing the inOrder method of central order traversal, if it is a branch node, it recursively executes the method of central order traversal (left subtree-> node itself-> right subtree ), then splice the three result lists; otherwise, an empty List is created for the leaf node.

In our impression, the implementation of polymorphism in traditional languages must be based on "classes and objects". In other words, the execution of an interface (or abstract class) can be determined at runtime) who is the object of the method ). However, in the pattern matching, this change point is moved to the function (or method). It seems that the implemented function is similar, but the two have their own advantages and disadvantages:

If the traditional method of polymorphism is used, and the thinking is based on classes and objects, the method is only a class or a class of object, and the method itself exists independently, it is meaningless, therefore, if a new implementation class is added, I need to implement all the methods in the new implementation class that need to be loaded/implemented interface (or abstract class, these methods are all concentrated in the newly added class/object. For example, if you write Java code to implement the above similar functions, I can define an interface Tree with the inOrder method, and then define the implementation class Node and Leaf respectively to implement this interface. This method is intuitive and cohesive when a new class is added. All the code is included in the newly added class and complies with the open/closed principle. However, if you want to add a new method to the interface, it will be finished, that is, the so-called "interface to be changed", and all the sub-classes must be modified. In Java 8, for the Lambda expression feature, some so-called pure and non-logical interfaces are provided, introduced the concept of "function interface"-allowed to exist "a non-java. lang. the abstract method defined in the Object ", which looks a bit like the behavior of your own face (the initial definition of the" interface "is to require it to be" pure ", there is no way to implement it). This is precisely because of the reason mentioned above-the interface does not have the ability to make changes openly. Now we need to add a default behavior to the interface and maintain backward compatibility, there is no such thing as Trait to graft other features, so we can only use this strange way to achieve it.
On the contrary, pattern matching changes the core point of attention to the function itself, and the function becomes a first-class citizen. It can exist independently of the class and object. If you want to add a class or object, it becomes very troublesome. You need to modify all existing related functions and add a case branch; however, if you want to add a method to a class or object, you only need to modify it in one place (in the above example, if you want to add the logic of first-order traversal, you only need to implement a "preOrder" function), and the added function is nested. Adding this modification conforms to the open and closed principle. Therefore, the two have their own advantages and disadvantages, depending on the design and application scenarios.
The above pattern matching methods can be combined to execute some complex matches, such as based on the constructor:


Case Node (_, Node (1, _, _), Node (2 ,_,_))

In this case, the value of the left subtree parameter is 1 and that of the three parameters of the constructor is 2.

You can even do this:

Case Node (_, nodeToReturn @ Node (1, _, _), Node (1, _, _) => nodeToReturn

Returns the second parameter of the constructor when this case is met.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.