2010-12-07 24 views
18

要創建可以在斯卡拉用於理解一個新的類,似乎所有你需要做的就是定義一個地圖功能:爲什麼過濾器必須在scala的for循環中爲模式匹配定義?

scala> class C[T](items: T*) { 
    | def map[U](f: (T) => U) = this.items.map(f) 
    | } 
defined class C 

scala> for (x <- new C(1 -> 2, 3 -> 4)) yield x 
res0: Seq[(Int, Int)] = ArrayBuffer((1,2), (3,4)) 

但是,只適用於簡單的for循環不存在模式匹配在<-的左側。如果您嘗試模式匹配那裏,你得到的是filter方法沒有定義投訴:

scala> for ((k, v) <- new C(1 -> 2, 3 -> 4)) yield k -> v 
<console>:7: error: value filter is not a member of C[(Int, Int)] 
     for ((k, v) <- new C(1 -> 2, 3 -> 4)) yield k -> v 

實現此匹配模式爲什麼需要過濾?我本來以爲會斯卡拉翻譯只是上述循環成等價的map電話:

scala> new C(1 -> 2, 3 -> 4).map{case (k, v) => k -> v} 
res2: Seq[(Int, Int)] = ArrayBuffer((1,2), (3,4)) 

,但似乎做工精細,所以for循環必須翻譯成別的東西。什麼是翻譯成需要filter方法?

回答

18

簡短的回答:根據Scala的規格,你不應該需要定義你給的例子一個「過濾器」的方法,但有一個open bug是手段它目前是必需的。

漫長的回答:適用於理解的desugaring算法在the Scala language specification中描述。讓我們先從第6.19節「對中的理解與For循環」(我看的規範版本2.9):

在第一個步驟,每一個發電機p < - e,其中p是不爭的(§ 8.1)對於e的類型替換爲p < - e.withFilter {case p => true; case _ => false}

您的問題的重要一點是,理解中的模式對於給定的表達式而言是否「無可辯駁」。 (該模式是'< - '之前的位;表達式是之後的位)。如果它是「無可辯駁的」,那麼withFilter將不會被添加,否則將需要它。

很好,但「無可辯駁」是什麼意思?跳到規範的第8.1.14節(「不可變模式」)。粗略地說,如果編譯器可以在匹配表達式時證明該模式不會失敗,那麼該模式是無可辯駁的,並且不會添加withFilter調用。

現在你的例子可以按照預期工作,它是第8.1.14節中的第一種無可辯駁的模式,一個變量模式。因此編譯器的第一個例子很容易確定withFilter不是必需的。

你的第二個例子可能是第三種無可辯駁的模式,一個構造模式。由於Int對於Any是無可辯駁的,所以試圖匹配()的(k,v)與Tuple2[Int,Int](參見說明書中的8.1.6和8.1.7))成功。因此,第二種模式也是無可辯駁的,並且不(不應該)需要withFilter方法。

在Daniel的例子中,Tuple2[Any,Any]Any不是無可辯駁的,所以withFilter調用被添加。

順便說一下,錯誤消息談到了一個filter方法,但規範談到withFilter - 它改變了與斯卡拉2.8,見this question and answer血淋淋的細節。

12

看到區別:

scala> for ((k, v) <- List(1 -> 2, 3 -> 4, 5)) yield k -> v 
res22: List[(Any, Any)] = List((1,2), (3,4)) 

scala> List(1 -> 2, 3 -> 4, 5).map{case (k, v) => k -> v} 
scala.MatchError: 5 
+0

我檢查了帶有和不帶'5'的編譯代碼。當沒有'5'時,不使用過濾器。 – IttayD 2010-12-07 19:56:50

相關問題