List 與 Tuple 模式


List可以用於模式比對,最基本的形式就是直接比較元素內容:
def what(a: Any) = a match {
case List(1) => "元素 1"
case List(1, 2) => "元素 1 2"
case List(1, 2, _) => "元素 1 2 以及第三個"
case _ => "別的東西"
}

println(what(List(1))) // 元素 1
println(what(List(1, 2))) // 元素 1 2
println(what(List(1, 2, 3))) // 元素 1 2 以及第三個
println(what(List(1, 2, 4))) // 元素 1 2 以及第三個

前兩個case完全比對List中的元素,第三個case因為結合了萬用字元模式,所以只要前兩個元素為1、2,而第三個元素不管是什麼都可以。下面則是另一個例子:
def what(a: Any) = a match {
case List(1, 2, x) => "元素 1 2 以及 " + x
case List(2, _*) => "首個元素 2 的 List"
case List(3, _*) => "首個元素 3 的 List"
case _ => "別的東西"
}

println(what(List(1, 2, 3))) // 元素 1 2 以及 3
println(what(List(1, 2, 4))) // 元素 1 2 以及 4
println(what(List(2, 2, 4))) // 首個元素 2 的 List
println(what(List(3, 2, 4, 5, 6))) // 首個元素 3 的 List

第一個case結合了變數模式,取得List第三個元素並指定給x。第二與第三個case使用了萬用字元模式,而*表示不管有幾個元素,所以第二個case比對的是,只要首個元素是2的List,第三個case比對的是只要首個元素是3的List。

List有head方法,可以傳回List中首個元素,也有個tail方法,可以傳回List除首個元素外,其它元素的陣列,例如:
scala> val list = List(1, 2, 3, 4, 5)
list: List[Int] = List(1, 2, 3, 4, 5)

scala> list.head
res0: Int = 1

scala> list.tail
res1: List[Int] = List(2, 3, 4, 5)

scala>


在模式比對中,可以使用::來同時取得head與tail,例如:
def what(a: Any) = a match {
case Nil => "空串列"
case head::tail => "首元素:" + head + ", 其它元素:" + tail
case _ => "別的東西"
}

println(what(List(1, 2, 3))) // 首元素:1, 其它元素:List(2, 3)
println(what(List(1, 2, 4))) // 首元素:1, 其它元素:List(2, 4)
println(what(List(2, 2, 4))) // 首元素:2, 其它元素:List(2, 4)
println(what(List(3, 2, 4, 5, 6))) // 首元素:3, 其它元素:List(2, 4, 5, 6)

::可以連續組合,最後一個是尾端所有元素,例如:
def what(a: Any) = a match {
case Nil => "空串列"
case x::y::tail => "首元素:" + x + ", 次元素:" + y + ", 其它元素:" + tail
case _ => "別的東西"
}

println(what(List(1, 2, 3))) // 首元素:1, 次元素:2, 其它元素:List(3)
println(what(List(1, 2, 4))) // 首元素:1, 次元素:2, 其它元素:List(4)
println(what(List(2, 2, 4))) // 首元素:2, 次元素:2, 其它元素:List(4)
println(what(List(3, 2, 4, 5, 6))) // 首元素:3, 次元素:2, 其它元素:List(4, 5, 6)

再來看看Tuple模式的運用:
def what(a: Any) = a match {
case (1, 2) => "(1, 2)"
case (_, _, x) => "三個元素 Tuple,第三個元素為 " + x
case _ => "別的東西"
}

println(what((1, 2))) // (1, 2)
println(what((1, 2, 4))) // 三個元素 Tuple,第三個元素為 4

同樣地,Tuple模式中還結合了萬用字元模式以及變數模式。事實上,模式還可以運用於變數指定,例如以下將函式傳回的Tuple元素指定給x、y、z的方式,就是一種模式運用:
def some = (1, 2, 3)

val (x, y, z) = some
println(x) // 1
println(y) // 2
println(z) // 3

val (a, b, _) = some
println(a) // 1
println(b) // 2

List也有同樣的應用:
def some = List(1, 2, 3, 4)

val List(w, x, y, z) = some
println(w) // 1
println(x) // 2
println(y) // 3
println(z) // 4

val List(a, _*) = some
println(a) // 1

val head::tail = some
println(head) // 1
println(tail) // List(2, 3, 4)

事實上,只要是 案例類別,也可以有類似的指定方式:
case class Point(x: Int, y: Int)
case class Circle(p: Point, r: Int)

def some = Point(1, 2)
def other = Circle(Point(3, 4), 5)

val Point(x, y) = some
println(x) // 1
println(y) // 2

val Circle(p, _) = other
println(p) // Point(3, 4)

事實上,List的head::tail模式比對,其實就是建構式模式比對,當你使用head::tail模式比對時,其實是在作::(head, tail)模式比對:
def some = List(1, 2, 3, 4)

val head1::tail1 = some
println(head1) // 1
println(tail1) // List(2, 3, 4)

val ::(head2, tail2) = some
println(head2) // 1
println(tail2) // List(2, 3, 4)

Scala確實定義了scala.:: 案例類別:
case final class ::[B](private hd : B, val tl : List[B]) extends List[B]

其實只要是案例類別,建構式模式都可以寫成中置運算形式,例如:
case class Point(x: Int, y: Int)
case class Circle(p: Point, r: Int)

def some = Point(1, 2)
def other = Circle(Point(3, 4), 5)

val x Point y = some
println(x) // 1
println(y) // 2

val p Circle _ = other
println(p) // Point(3, 4)

最主要的是寫成這種形式,對可讀性有無幫助,scala.:: 建構式模式比對時,寫為head::tail形式,主要是可對應至List所提供的::方法,因此寫為中置運算形式直覺且易讀。

在for迴圈中,也可以運用模式,例如:
val list1 = List((101, "Justin"), (102, "momor"))
for((room, name) <- list1) {
printf("%d, %s%n", room, name)
}

case class Point(x: Int, y: Int)
val list2 = List(Point(0, 0), Point(1, 1), Point(2, 2))
for(Point(x, y) <- list2) {
printf("%d, %d%n", x, y)
}

現在你應該可以看懂 match 運算式 最後例子中的模式是怎麼回事了:
object Sort {
def quick(list: List[Int]): List[Int] = {
list match {
case Nil => Nil
case x::xs =>
val (before,after) = xs partition (_ < x)
quick(before) ++ (x :: quick(after))
}
}
}