2014-09-20 30 views
5

是怎樣的頭部和尾部在下面的語句來確定:Scala Cons模式匹配如何確定List的頭部和尾部?

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

不應該有一些代碼段,用於提取第一元素作爲頭,並返回尾巴作爲一個新的列表。我一直在梳理Scala標準庫代碼,我找不到/瞭解如何/在哪裏完成。

+4

http://blog.lunatech.com/2011/11/25/scala-list-extractor-demystified很好的解釋它是如何工作的 – 2014-09-20 02:45:07

+0

@ eugene-zhulenev,謝謝你的鏈接。這是現貨。 Cons案例類自動提供了構造List實例並將其解構爲頭部和尾部的方法。 – poissondist 2014-09-20 03:03:13

+0

@poissondist來自eugene zhulenev的鏈接已損壞。你如何看待我的答案? – metasim 2016-06-14 21:45:45

回答

0

此處涉及的Scala構造是Extractor。符號::不過是Scala中的一個案例類,其伴侶對象上存在一個unapply方法,以使提取魔法發生。 Here是關於提取器的深入教程。但是總結如下:

無論何時你想「解壓縮」一個類的內容,無論是用於變量綁定還是作爲模式匹配的一部分,編譯器都會在左側的任何符號上尋找方法unapply的表達。這可能是一個對象,一個case類伴侶對象(如::,在你的問題中),或者一個帶有unapply的實例。 unapply的參數是要解包的傳入類型,返回類型是已聲明爲預期結構和類型的Option。在模式匹配中,None表示未找到匹配項。在變量綁定中,如果None是結果,則引發MatchError

關於unapply的一個好方法是它與apply相反。其中unapply是函數調用語法的接收者,unapply是提取者調用的接收者。

爲了進一步說明這一點,讓我們定義一個簡單的例子類:

case class Cat(name: String, age: Int) 

因爲它是一個案例類,我們得到自動產生的同伴對象,這大致是這樣的applyunapply方法:

object Cat { 
    // compiler generated... 
    def apply(name: String, age: Int) = new Cat(name, age)  
    def unapply(aCat: Cat): Option[(String, Int)] = Some((aCat.name, aCat.age)) 
} 

當您通過伴隨對象創建Cat時,將調用apply。當您打開一個Cat的組成部分,unapply被稱爲:

val mycat = Cat("freddy", 3) // `apply` called here 
... 
val Cat(name, age) = mycat // `unapply` called here 
... 
val animal: AnyRef = mycat 
val info = animal match { 
    case Cat(name, age) => "My pet " + name // `unapply` called here 
    case _ => "Not my pet" 
} 
// info: String = My pet freddy 

因爲unapply返回一個Option,我們有大量的電力來編寫處理更有趣的情況下,例如提取,檢測是否進入的類型在提取值之前符合一些標準。例如,假設我們想要得到「舊」貓的名字。有人可能會做到這一點:

object OldCatName { 
    def unapply(aCat: Cat) = if (aCat.age >= 10) Some(aCat.name) else None 
} 

用法是相同的生成unapply

val yourcat = Cat("betty", 12) 
... 
val OldCatName(name1) = yourcat 
// name1: String = "betty" 
val OldCatName(name2) = mycat 
// scala.MatchError: Cat(freddy,3) (of class Cat) 

MatchError s爲不是一件好事,讓,讓我們使用模式匹配:

val conditions = Seq(mycat, yourcat) map { 
    case OldCatName(oldie) => s"$oldie is old" 
    case Cat(name, age) => s"At age $age $name is not old" 
} 
// conditions: Seq[String] = List(At age 3 freddy is not old, betty is old) 

unapply方法::有關的一個額外的魔法是一些語法糖允許val ::(head, tail) = ...改寫爲val head :: tail = ...