1. 程式人生 > >話說模式匹配(2) scala裡是怎麼實現的?

話說模式匹配(2) scala裡是怎麼實現的?

在這篇martin和另外兩位模式匹配領域專家的論文裡說了模式匹配的幾種實現方式,以及scala是選擇哪種方式來實現的。
http://lampwww.epfl.ch/~emir/written/MatchingObjectsWithPatterns-TR.pdf
我引用了裡面的一些描述。

在面向物件的程式中資料被組織為一級一級的類(class)
面嚮物件語言在模式匹配方面的問題在於如何從外部探測這個層級。

有6種實現模式匹配的方法:
1) 面向物件的分解 (decomposition)
2) 訪問器模式 (visitor)
3) 型別測試/型別造型 (type-test/type-cast)
4) typecase
5) 樣本類 (case class)
6) 抽取器 (extractor)

論文裡從3個維度9個標準來對比了各種實現方式:
簡明程度(框架支援、淺匹配、深匹配),維護性(表徵獨立、擴充套件性),效能(基礎效能、廣度和深度延展性)


比較的細節在這篇論文裡有提,不一一展開。
最終scala選擇了採用 樣本類(case class) 和 抽取器(extractor) 來實現模式匹配。

我們大致瞭解一下case class和extractor 是怎麼回事

1)樣本類(case class)

本質上case class是個語法糖,對你的類構造引數增加了getter訪問,還有toString, hashCode, equals 等方法;
最重要的是幫你實現了一個伴生物件,這個伴生物件裡定義了apply 方法和 unapply 方法。
apply方法是用於在構造物件時,減少new關鍵字;而unapply方法則是為模式匹配所服務。
這兩個方法可以看做兩個相反的行為,apply是構造(工廠模式),unapply是分解(解構模式)。

case class在暴露了它的構造方式,所以要注意應用場景:當我們想要把某個型別暴露給客戶,但又想要隱藏其資料表徵時不適宜。

2) 抽取器(extrator)

抽取器是指定義了unapply方法的object。在進行模式匹配的時候會呼叫該方法。
unapply方法接受一個數據型別,返回另一資料型別,表示可以把入參的資料解構為返回的資料。

比如
class A
class B(val a:A)
object TT {
def unapply(b:B) = Some(new A)
}
這樣定義了抽取器TT後,看看模式匹配:val b = new B(new A); b match{ case TT(a) => println(a) }
直觀上以為 要拿b和TT型別匹配,實際被翻譯為 TT.unapply(b)  match{ case Some(…) => … }

它與上面的case class相比,相當於自己手動實現unapply,這也帶來了靈活性。
後續會專門介紹一下extrator,這裡先看一下extractor怎麼實現case class無法實現的”表徵獨立”(representation independence)

比如我們想要暴露的型別為A

//定義為抽象型別
trait A

//然後再實現一個具體的子類,有2個構造引數
class B (val p1:String, val p2:String) extends A

//定義一個抽取器
object MM{

//抽取器中apply方法是可選的,這裡是為了方便構造A的例項
def apply(p1:String, p2:String) : A = new B(p1,p2);

//把A分解為(String,String)
def unapply(a:A) : Option[(String, String)] = {
if (a.isInstanceOf[B]) {
val b = a.asInstanceOf[B]
return Some(b.p1, b.p2)
}
None
}
}

這樣客戶只需要通過 MM(x,y) 來構造和模式匹配了。客戶只需要和MM這個工廠/解構角色打交道,A的實現怎麼改變都不受影響。

注:
有很多的資料裡在介紹case class時經常把它和函式式語言裡的代數資料類對比(ADT)
嚴格的說Scala中的case class並不是ADT,但比較靠近,可以模擬ADT。
這篇文章中提到case class介於類繼承和代數資料型別之間 http://blog.csdn.net/jinxfei/article/details/4677359
提到:”Scala則提供了一種介於兩者之間(類繼承和代數資料型別),被稱為條件類(case classes)的概念”

《Programming in Scala》中文版,在術語表中有提到ADT:
通過提供若干個含有獨立構造器的備選項(alternative)來定義的型別。通常可以輔助於通過模式匹配解構型別的方式。
這個概念可以在規約語言和函式式語言中發現。代數資料型別在Scala中可以用樣本類(case class)模擬。