Skip to content
matt90luo's Blog
Go back

scala-模式匹配

> 模式匹配是检查某个值(value)是否匹配某一个模式的机制,一个成功的匹配同时会将匹配值解构为其组成部分。它是Java中的switch语句的升级版,同样可以用于替代一系列的 if/else 语句。

scala的模式匹配是其非常出彩的一个点。

<!— more —>

模式匹配语法

一个模式匹配语句包括一个待匹配的值,match关键字,以及至少一个case语句。 match 对应 Java 里的 switch,但是写在选择器表达式之后。即: 选择器 match {备选项}。 match 表达式通过以代码编写的先后次序尝试每个模式来完成计算,只要发现有一个匹配的case,剩下的case不会继续匹配。

import scala.util.Random

val x: Int = Random.nextInt(10)

x match &#123;
  case 0 =&gt; "zero"
  case 1 =&gt; "one"
  case 2 =&gt; "two"
  case _ =&gt; "other"
&#125;

上述代码中的val x是一个0到10之间的随机整数,将它放在match运算符的左侧对其进行模式匹配,match的右侧是包含4条case的表达式,其中最后一个case _表示匹配其余所有情况,在这里就是其他可能的整型值。

def matchTest(x: Int): String = x match &#123;
  case 1 =&gt; "one"
  case 2 =&gt; "two"
  case _ =&gt; "other"
&#125;
matchTest(3)  // other
matchTest(1)  // one

这个match表达式是String类型的,因为所有的情况(case)均返回String,所以matchTest函数的返回值是String类型。

模式匹配种类

通配符匹配

/**
    * 通配符匹配
    * @param x
    * @return
    */
  def m1(x: Any) = x match &#123;
    case List(0, _, _) =&gt; "匹配 0 元素开头的list"
    case List(1, _*) =&gt; "匹配 1 元素开头的list"
    case Vector(1, _*) =&gt; "匹配 1 元素开头的vector"
    case m: Map[_, _] =&gt; m.toString
    case _ =&gt; "Unknown"
  &#125;

  println(m1(List(0,1,2))) //匹配 0 元素开头的list
  println(m1(List(1,2))) //匹配 1 元素开头的list
  println(m1(Vector(1,2,3))) //匹配 1 元素开头的vector

常量匹配

def m2(x:Any) = x match &#123;
  case 0 =&gt; println("zero")
  case true =&gt; println("true")
  case "hello" =&gt; println("you said 'hello'")
  case Nil =&gt; println("an empty List")
  case _ =&gt; println("unknow")
&#125;

println(m2("hello")) //you said 'hello'
println(m2(true)) //true

变量匹配

val a = 20
val b = 30
20 match &#123; case a =&gt; 1 &#125; // 1, a是模式变量,不是10
//为了使用变量a,必须用`a`:
20 match &#123; case `a` =&gt; 1; case `b` =&gt; -1 &#125; // -1,`a`是变量10
//或者用大写的变量

构造函数匹配

构造器模式不只检查顶层对象是否一致,还会检查对象的内容是否匹配内层的模式。由于额外的模式自身可以形成构造器模式,因此可以使用它们检查到对象内部的任意深度。

    sealed trait Animal
    case class Cat(name: String, color: String) extends Animal
    case class Dog(name: String, color: String, age: Int) extends Animal

    def what(animal: Animal): String = animal match &#123;
      case Cat(name: String, color: String) =&gt; s"cat's is <Latex formula="name,color" inline />color"
      case Dog(name: String, color: String, age: Int) =&gt; s"dog's is <Latex formula="name,color" inline />color ,age $age"
    &#125;
    val a1 = Cat("多啦A梦", "白色")
    val s1 = what(a1)
    println(s1) //a cat name is 多啦A梦,color is 白色

    val a2 = Dog("多啦B梦", "白色", 500)
    val s2 = what(a2)
    println(s2) //a dog name is 多啦B梦,color is 白色 ,age is 500

集合类型匹配

数组匹配

def m5(arr: Array[Int]) = arr match &#123;
    case Array(1, x, y) =&gt; println("匹配以1 开头,有三个元素的数组");
    case Array(0) =&gt; println("匹配只有 0 这个元素的数组");
    case Array(0, _*) =&gt; println("匹配以0 开头任意多个元素的数组");
    case arr if arr.length == 2 =&gt; println("length = 2")
    case _ =&gt; println("unknow")
&#125;

序列匹配

def m5_1(list: List[Int]) = list match &#123;
    case 5 :: Nil =&gt; println("匹配只有 5 这个元素的序列")
    case x :: y :: Nil =&gt; println("匹配只有两个元素的序列")
    case list if list.last == 1 =&gt; println("结尾是1的列表")
    case x :: tail =&gt; println("匹配任意多个元素的数组")
    case _ =&gt; println("unknow")
&#125;

m5_1(List(5)) //匹配只有 5 这个元素的序列
m5_1(List(1,2)) //匹配只有两个元素的序列
m5_1(List(1,2,3,4,5,1)) //结尾是1的列表

tuple类型匹配

def m6(tuple: Any) = tuple match &#123;
    case (x, y, 7) =&gt; println("匹配有三个元素并且以7 结尾的元组");
    case (2, x, y) =&gt; println("匹配以2 开头有三个元素的元组");
    case _ =&gt; println("unknow")
&#125;

println(m6((1,2,7))) //匹配有三个元素并且以7 结尾的元组
println(m6((2,2,0))) //匹配以2 开头有三个元素的元组

类型匹配

def m7(x: Any): String = x match &#123;
    case x: String =&gt; x
    case x: Int if x &gt; 5 =&gt; x.toString //带if守卫条件的匹配
    case _ =&gt; "unknow"
&#125;

println(m7("hello")) //hello
println(m7(9)) // 9 
println(m7(2)) // unknow ,(虽然2满足Int类型, 但是不满足守卫条件"大于5",所以往下匹配)


def m7_1(v: Any) = v match &#123;
    case null =&gt; "null"
    case i: Int =&gt; i * 100
    case s: String =&gt; s
    case _ =&gt; "others"
&#125;
// 注意:上面case中的i、s都叫模式变量
println(m7_1(null)) // "null"
println(m7_1(5)) // 500
println(m7_1("hello")) // "hello"
println(m7_1(3.14)) // "others"

模式匹配的应用

递归

def fac1(n:Int):Int = n match &#123;
    case 0 =&gt; 1 
    case _ =&gt; n * fac1( n - 1 )
&#125;
fac1(5) //120

// 同
def fac2: Int =&gt; Int = &#123;
    case 0 =&gt; 1
    case n =&gt; n * fac2( n - 1 )
&#125;
fac2(5) //120

// 同 使用尾递归
def fac3: (Int,Int) =&gt; Int = &#123;
    case (0,y) =&gt; y
    case (x,y) =&gt; fac3(x-1, x*y)
&#125;
fac3(5,1) // 120

// 同 reduceLeft
def fac4(n:Int) = 1 to n reduceLeft( _ * _ )

implicit def foo(n:Int) = new &#123; def ! = fac4(n) &#125;
5! // 120

// 同
def fac5(n:Int) = (1:BigInt) to n product
fac5(5) // 120

模式匹配和二叉数遍历

利用scala的模式匹配和递归可以轻松实现二叉树的遍历

/**
 *       1
 *   2      3
 * 4  5       6
 *   7 8
 */
object bTreeExample &#123;

  abstract class BinaryTree

  case class Node(value: Int,
                  left: BinaryTree = null, right: BinaryTree = null) extends BinaryTree

  def traverse(t: BinaryTree): Unit = &#123;
    t match &#123;
      case null =&gt; Unit
      case Node(v, left, right) =&gt; traverse(left);println(v);traverse(right) // 中序
      //case Node(v, left, right) =&gt; println(v); traverse(left); traverse(right) //前序
      //case Node(v, left, right) =&gt; traverse(left);traverse(right);println(v)  //后序
    &#125;
  &#125;

  def BFS(t: (List[Node], List[Int])): (List[Node], List[Int]) = t match &#123;
    case (Nil, res) =&gt; (Nil, res)
    case (lB, res) =&gt; &#123;
      val tmp = lB.map &#123; case Node(v, null, null) =&gt; (Nil, v)
      case Node(v, left, null) =&gt; (List(left.asInstanceOf[Node]), v)
      case Node(v, null, right) =&gt; (List(right.asInstanceOf[Node]), v)
      case Node(v, left, right) =&gt; (List(left.asInstanceOf[Node], right.asInstanceOf[Node]), v)
      &#125;
      BFS((tmp.flatMap(_._1), res ++ tmp.map(_._2)))
    &#125;
  &#125;

  def DFS(t:(List[Node], List[Int])): (List[Node], List[Int]) =t match&#123;
    case (Nil, res) =&gt; (Nil, res)
    case (lB, res) =&gt; &#123;
      val top = lB.head
      val left = if(top.left == null) Nil else List(top.left.asInstanceOf[Node])
      val right = if(top.right == null) Nil else List(top.right.asInstanceOf[Node])
      DFS(left ++ right ++ lB.tail, res ++ List(top.value))
    &#125;
  &#125;

  def main(args: Array[String]): Unit = &#123;
    val t = Node(1, Node(2, Node(4), Node(5, Node(7), Node(8))), Node(3, null, Node(6)))
    //traverse(t)
    val res = DFS((List(t), List.empty[Int]))
    println(res._2)  // List(1, 2, 4, 5, 7, 8, 3, 6)

    //val res = DFS((List(t), List.empty[Int]))
    //println(res._2) // List(1, 2, 4, 5, 7, 8, 3, 6)
  &#125;

&#125;

Share this post on:

Previous Post
推荐系统-YouTubeDNN详解
Next Post
推荐系统-基于FM模型的召回