變數綁定(Variable binding)、模式防護(Pattern guard)


變數綁定(Variable binding)可以在模式符合時,讓你指定任意位置的值給變數。直接看例子比較清楚,如果有個程式片段如下:
def what(list: List[Int]) = list match {
case List(2, _*) => list.tail
case List(3, _*) => list.tail
case _ => Nil
}

println(what(List(2, 3, 4, 5))) // List(3, 4, 5)
println(what(List(3, 4, 5, 6))) // List(4, 5, 6)
println(what(List(1, 3, 4, 5))) // List()

如果改用變數綁定的方式,可以如下撰寫:
def what(list: List[Int]) = list match {
case List(2, tail @ _*) => tail
case List(3, tail @ _*) => tail
case _ => Nil
}

println(what(List(2, 3, 4, 5))) // List(3, 4, 5)
println(what(List(3, 4, 5, 6))) // List(4, 5, 6)
println(what(List(1, 3, 4, 5))) // List()

當List中的元素是以2或3開頭,不論其後面有幾個元素時,可以符合上例中的兩個case,一旦模式符合,就將首個元素外的其它元素指定給tail變數。

下面這個例子則比對圓心是在(0, 0)的柱子,若找到則傳回圓的資訊:
case class Point(x: Int, y: Int)
case class Circle(p: Point, r: Int)
case class Cylinder(c: Circle, h: Int)

def what(c: Cylinder) = c match {
case Cylinder(circle @ Circle(Point(0, 0), _), _) => circle
case _ => null
}

val cy1 = Cylinder(Circle(Point(0, 0), 10), 10)
val cy2 = Cylinder(Circle(Point(0, 0), 20), 20)
val cy3 = Cylinder(Circle(Point(1, 1), 20), 20)
println(what(cy1)) // Circle(Point(0,0),10)
println(what(cy2)) // Circle(Point(0,0),20)
println(what(cy3)) // null

如果不使用變數綁定,基本上可以這麼寫:
case class Point(x: Int, y: Int)
case class Circle(p: Point, r: Int)
case class Cylinder(c: Circle, h: Int)

def what(c: Cylinder) = c match {
case Cylinder(Circle(Point(0, 0), _), _) => c.c
case _ => null
}

val cy1 = Cylinder(Circle(Point(0, 0), 10), 10)
val cy2 = Cylinder(Circle(Point(0, 0), 20), 20)
val cy3 = Cylinder(Circle(Point(1, 1), 20), 20)
println(what(cy1)) // Circle(Point(0,0),10)
println(what(cy2)) // Circle(Point(0,0),20)
println(what(cy3)) // null

模式防護(Pattern guard)則可以讓你在比對模式成功後,進一步設定判斷條件,決定是否執行=>之後的程式。例如下面這個範例可以找出圓心座標x等y的圓,並傳回其半徑,否則傳回圓本身:
case class Point(x: Int, y: Int)
case class Circle(p: Point, r: Int)

def what(c: Circle) = c match {
case Circle(Point(x, y), r) if x == y => r
case _ => c
}

println(what(Circle(Point(1, 1), 10))) // 10
println(what(Circle(Point(2, 2), 20))) // 20
println(what(Circle(Point(3, 1), 10))) // Circle(Point(3,1),10)
println(what(Circle(Point(4, 4), 30))) // 30

如果不用模式防護,基本上是可以寫成以下的方式:
case class Point(x: Int, y: Int)
case class Circle(p: Point, r: Int)

def what(c: Circle) = c match {
case Circle(Point(x, y), r) => if(x == y) r else c
}

println(what(Circle(Point(1, 1), 10))) // 10
println(what(Circle(Point(2, 2), 20))) // 20
println(what(Circle(Point(3, 1), 10))) // Circle(Point(3,1),10)
println(what(Circle(Point(4, 4), 30))) // 30