Pattern Matching
来源:互联网 发布:什么是bim软件 编辑:程序博客网 时间:2024/06/05 12:45
1. Values, Variables, and Types in Matches
(1) Basic
Let’s cover several kinds of matches
. The following example matches on specific values, all values of specific types, and it shows one way of writing a “default” clause that matches anything:
code example
object Hello { def main(args: Array[String]): Unit = { for (x <- Seq(1, 2, 2.7, "one", "two", 'four)) { val str = x match { case 1 => "int 1" case i: Int => "other int: "+i // 假设匹配此句, i=x case d: Double => "a double: "+x case "one" => "string one" case s: String => "other string: "+s case unexpected => "unexpected value: "+unexpected } println(str) } }}
Note:
- Because
x
is of typeAny
, we need enough clauses to cover all possible values. That’s why the “default” clause (withunexpected
) is needed. - Matches are eager(及早的), so more specific clauses must appear before less specific clauses.(如果
case 1 => "int 1"
和case i: Int => "other int: "+i
交换顺序,则case 1
将永远不会匹配). - Matching on floating-point literals is a bad idea, as rounding errors mean two values that appear to be the same often differ in the least significant digits.
(2) Using Placeholder _
We can replace the variables i
, d
, s
, and unexpected
with the placeholder _
.
object Hello { def main(args: Array[String]): Unit = { for (x <- Seq(1, 2, 2.7, "one", "two", 'four)) { val str = x match { case 1 => "int 1" case _: Int => "other int: "+x case _: Double => "a double: "+x case "one" => "string one" case _: String => "other string: "+x case _ => "unexpected value: "+x } println(str) } }}
(3) Using defined variable after case
In case
clauses, a term that begins with a lowercase letter is assumed to be the name of a new variable that will hold an extracted value. To refer to a previously defined variable, enclose it in back ticks. Conversely, a term that begins with an uppercase letter is assumed to be a type name.
object Hello { def main(args: Array[String]): Unit = { checkY(100) } def checkY(y :Int): Unit = { for (x <- Seq(99, 100, 101)) { val str = x match { case `y` => "found y!" // 如果你使用y,而不是`y`,则它并不是函数参数y case i: Int => "int: "+i } println(str) } }}
(4) Using |
Sometimes we want to handle several different matches with the same code body. To avoid duplication, we could refactor the code body into a method, but case
clauses also support an “or” construct, using a |
method:
code example
object Hello { def main(args: Array[String]): Unit = { for (x <- Seq(1, 1.2, 3, "haha")) { val str = x match { case _: Int | _: Double => "number: "+x case _ => "string: "+x } println(str) } }}
output
number: 1number: 1.2number: 3string: haha
2. Matching Sequences
(1) Matching Sequences
Seq
(for “sequence”) is a parent type for the concrete collection types that support iteration over the elements in a deterministic order, such as List
and Vector
.
Let’s examine the classic idiom for iterating through a Seq
using pattern matching and recursion, and along the way, learn some useful fundamentals about sequences:
object Hello { def main(args: Array[String]): Unit = { val nonEmptySeq = Seq(1, 2, 3, 4, 5) val emptySeq = Seq.empty[Int] val nonEmptyList = List(1, 2, 3, 4, 5) val emptyList = Nil val nonEmptyVector = Vector(1, 2, 3, 4, 5) val emptyVector = Vector.empty[Int] val nonEmptyMap = Map("one"->1, "two"->2, "three"->3) val emptyMap = Map.empty[String, Int] val newSeq = Seq(nonEmptySeq, emptySeq, nonEmptyList, emptyList, nonEmptyVector, emptyVector, nonEmptyMap.toSeq, emptyMap.toSeq) // (4) for (seq <- newSeq){ println(seqToString(seq)) } } def seqToString[T](seq: Seq[T]): String = { // (1) seq match { case head +: tail => s"$head +: "+seqToString(tail) // (2) case Nil => "Nil" // (3) } }}
Explain:
(1)
Define a recursive method that constructs aString
from aSeq[T]
for some typeT
. The body is one expression that matches on the inputSeq[T]
。(2)
There are twomatch
clauses and they are exhaustive. The first matches on any nonemptySeq
, extracting thehead
, the first element, and thetail
, which is the rest of theSeq
. (Seq
hashead
andtail
methods, but here these terms are interpreted as variable names as usual forcase
clauses.) The body of the clause constructs aString
with thehead
followed by+:
followed by the result of callingseqToString
on thetail
.(3)
The only other possiblecase
is an emptySeq
. We can use the special object for empty Lists,Nil
, to match all the empty cases. Note that anySeq
can always be interpreted as terminating with an empty instance of the same type, although only some types, likeList
, are actually implemented that way.(4)
Put theSeqs
in anotherSeq
(callingtoSeq
on the Maps to convert them to a sequence of key-value pairs), then iterate through it and print the results of callingseqToString
on each one.
Note:
- The operator
+:
is the “cons”(construction) operator for sequences. It is similar to the::
operator forLists
. - The terms
head
andtail
are arbitrary variable names. That’s mean that you can replace them withx
andy
. - If your sequence is
List
,you can replace+:
with::
. But it’s more conventional now to write code that usesSeq
, so it can be applied to all subclasses, includingList
andVector
.
(2) Reconstruct List
, Map
We can use +:
and ::
restruct List
:
scala> val s1 = (1 +: (2 +: (3 +: (4 +: (5 +: Nil)))))s1: List[Int] = List(1, 2, 3, 4, 5)scala> val l = (1 :: (2 :: (3 :: (4 :: (5 :: Nil)))))l: List[Int] = List(1, 2, 3, 4, 5)scala> val s2 = (("one",1) +: (("two",2) +: (("three",3) +: Nil)))s2: List[(String, Int)] = List((one,1), (two,2), (three,3), (four,4))scala> val m = Map(s2 :_*)m: scala.collection.immutable.Map[String,Int] = Map(one -> 1, two -> 2, three -> 3, four -> 4)
Note that the Map.apply
factory method expects a variable argument list of two-element tuples. So, in order to use the sequence s2
to construct a Map
, we had to use the :_*
idiom for the compiler to convert it to a variable-argument list.
So, there’s an elegant symmetry between construction and pattern matching (“deconstruction”) when using +:
and ::
.
3. Matching on Tuples
Tuples are so easy to match on:
object Hello { def main(args: Array[String]): Unit = { val langs = Seq( ("Scala", "Martin", "Odersky"), ("Clojure", "Rich", "Hickey"), ("Lisp", "John", "McCarthy")) for (tuple <- langs) { tuple match { case ("Scala", _, _) => println("Found Scala") case (lang, firstName, lastName) => println(s"Found other language: $lang, $firstName $lastName") } } }}
output
Found ScalaFound other language: Clojure, Rich HickeyFound other language: Lisp, John McCarthy
A tuple
can be taken apart into its constituent elements. We can match on literal values within the tuple
, at any positions we want, and we can ignore elements we don’t care about.
4. Guards in case
Clauses
Code Example:
object Hello { def main(args: Array[String]): Unit = { val list = List(1, 2, 3, 4, 5) for (value <- list) { value match { case _ if value%2 == 0 => println(s"even: $value") case _ => println(s"odd: $value") } } }}
Output:
odd: 1even: 2odd: 3even: 4odd: 5
5. Maching on case
Classes
(1) Deep Matching
Let’s see more examples of deep matching, where we examine the contents of instances of case
classes:
Code Example:
case class Address(street: String, city: String, country: String)case class Person(name: String, age: Int, address: Address)val alice = Person("Alice", 25, Address("1 Scala Lane", "Chicago", "USA"))val bob = Person("Bob", 29, Address("2 Java Ave.", "Miami", "USA"))val charlie = Person("Charlie", 32, Address("3 Python Ct.", "Boston", "USA"))for (person <- Seq(alice, bob, charlie)) { person match { case Person("Alice", 25, Address(_, "Chicago", _) => println("Hi Alice!") case Person("Bob", 29, Address("2 Java Ave.", "Miami", "USA")) => println("Hi Bob!") case Person(name, age, _) => println(s"Who are you, $age year-old person named $name?") }}
Output:
Hi Alice!Hi Bob!Who are you, 32 year-old person named Charlie?
Note that we could match into nested types.
- Pattern Matching
- perl---Pattern-Matching Operators
- CareerCup-PATTERN MATCHING
- 12c -- Pattern Matching
- suffix tree pattern matching
- PostgreSQL Pattern Matching
- mysql pattern matching
- [Linux]--Pattern Matching
- 12 Pattern Matching
- Boyer Moore Pattern Matching Algorithm
- Flexible Pattern Matching in Strings
- Pattern Matching Metacharacters For asm_diskstring
- 神奇的Scala Pattern Matching
- WEEK3-Lists and pattern matching
- Pattern matching around type erasure
- Introduction to Regexes and Pattern Matching
- Pattern matching on Java objects using Tom
- Equations: a dependent pattern-matching compiler
- WebSocket的进一步实例
- 自定义折线图
- yii2.0应用介绍
- GITHUB自学系列之一「初识 GITHUB」
- IOS百度地图屏幕坐标与经纬度坐标的转换
- Pattern Matching
- 01作业
- 使用angularjs1.x构建前台开发框架(二)——require的使用
- iOS开发-事件传递与响应者链中的hitTest方法和pointInside方法
- 大整数乘法CPP
- 周详:重价救赎 · 《天下无贼》电影与王宝强离婚风波
- C++ 取代switch的三种方法
- 【操作系统】磁盘调度
- 周详:师生之道 ·《西游记》与德云社师徒大战 ︱ 中法评