2011-08-23 70 views
12

在Scala中,你可以做在Scala中如何忽略匹配詞?

list.filter { item => 
    item match { 
     case Some(foo) => foo.bar > 0 
    } 
} 

但是你也可以做到通過省略match更快的方式:

list.filter { 
    case Some(foo) => foo.bar > 0 
} 

這是如何支持Scala呢?這是2.9的新功能嗎?我一直在尋找它,我可以弄清楚是什麼讓這成爲可能。它只是Scala編譯器的一部分嗎?

+0

事實上,你的第一個變種是無效的。 item沒有定義(應該是,它並不意味着列表中的一個元素)。過濾器期望一個函數,你必須編寫list.filter {item => item match ...這不僅僅是匹配消失。 –

+1

@didierd,我編輯了這個問題來解決你的問題,因爲我認爲這裏仍然有一個有趣的問題。 –

+0

@kipton感謝您的更新。 –

回答

13

language specification解決了8.5節中的問題。的相關部分:

匿名函數可德音響通過箱子

{ case p1 => b1 ... case pn => bn } 

如果預期的類型是scala.Functionk[S1, ..., Sk, R],表達被認爲是 的序列定義爲等價於匿名功能:

(x1 : S1, ..., xk : Sk) => (x1, ..., xk) match { 
    case p1 => b1 ... case pn => bn 
} 

如果預期的類型爲scala.PartialFunction[S, R],表達取爲 等同於以下實例創建表達式:

new scala.PartialFunction[S, T ] { 
    def apply(x: S): T = x match { 
    case p1 => b1 ... case pn => bn 
    } 
    def isDefinedAt(x: S): Boolean = { 
    case p1 => true ... case pn => true 
    case _ => false 
    } 
} 

所以,鍵入或表達PartialFunction一個Function影響表達是如何編譯。

trait PartialFunction [-A, +B] extends (A) ⇒ B所以部分功能PartialFunction[A,B]也是Function[A,B]

+0

我喜歡這個答案。 –

+0

是的,我認爲這很好地回答了原來的問題。更多信息:當期望的類型是一個函數時,如'Seq.filter'的情況,這解釋瞭如何使用'{case ...}'。對於其他方法,比如'Seq.collect',預期的類型是PartialFunction,然後表達式'{case ...}'*應該是必需的,而不是'x => x match {case ... }';讓 - 菲利普的答案處理這種情況,但請注意我的答案中描述的奇怪的編譯器行爲。 –

+0

接受這個答案作爲接受的答案,因爲它涵蓋了編譯器做得非常好。謝謝大家的答案。 –

18

編輯:這個答案的一部分是錯誤的;請參閱huynhjl's answer


如果省略match,則發信號通知要定義部分功能編譯器。部分函數是沒有爲每個輸入值定義的函數。例如,您的過濾器功能僅針對Some[A]類型的值(針對您的自定義類型A)進行定義。

PartialFunction當您嘗試將它們應用到未定義的位置時,會拋出MatchError。因此,當你通過PartialFunction定義的常規Function時,你應該確保你的部分函數永遠不會被調用。這種機制非常有用,例如在集合中拆包元組:

val tupleSeq: Seq[(Int, Int)] = // ... 
val sums = tupleSeq.map { case (i1, i2) => i1 + i2 } 

的API,其要求的部分功能,如在集合上collect過濾器般的操作,通常把部分功能之前調用isDefinedAt。在那裏,它是安全的(並且經常需要)具有不是爲每個輸入值定義的部分功能。

所以你看到儘管語法接近於match的語法,但它實際上是我們正在處理的完全不同的事情。

+0

你說什麼是有道理的,但根據我在答案中發佈的測試,這兩個表達式似乎表現得完全一樣。它可能是一個錯誤? –

+0

@Kipton,事實證明'{case ...}'可以是'x match {case ...}'匿名函數的語法糖 - 請參閱我的答案。 – huynhjl

+0

@huynhjl,據我所知,編譯器將'{case ...}'轉換爲形式爲'x => x match {case ...}'的函數。奇怪的是,編譯器也做了相反的處理:將'x => x match {case ...}'轉換爲PartialFunction。我很確定這不是在規範中,還有一個編譯器錯誤。查看我的答案瞭解更多詳情。 –

6

- 修訂後 -

嗯,我不知道我看到的差異,斯卡拉2.9.1.RC3,

val f: PartialFunction[Int, Int] = { case 2 => 3 } 
f.isDefinedAt(1) // evaluates to false 
f.isDefinedAt(2) // evaluates to true 
f(1) // match error 

val g: PartialFunction[Int, Int] = x => x match { case 2 => 3 } 
g.isDefinedAt(1) // evaluates to false 
g.isDefinedAt(2) // evaluates to true 
g(1) // match error 

看來fg行爲都一模一樣與PartialFunctions相同。

這裏是另一個例子展示了等價:更有意思的

Seq(1, "a").collect(x => x match { case s: String => s }) // evaluates to Seq(a) 

// this compiles 
val g: PartialFunction[Int, Int] = (x: Int) => {x match { case 2 => 3 }} 

// this fails; found Function[Int, Int], required PartialFunction[Int, Int] 
val g: PartialFunction[Int, Int] = (x: Int) => {(); x match { case 2 => 3 }} 

所以這是在編譯器級別的一些特殊套管x => x match {...},只是{...}之間的轉換。

更新。在閱讀語言規範後,這對我來說似乎是一個錯誤。我在錯誤跟蹤器中提交了SI-4940

+0

你的意思是g.isDefinedAt而不是f? g還是一個PartialFunction? –

+0

'g'和'f'都有'PartialFunction'類型,是的。 –

+0

是的,然後我只是困惑如何斯卡拉知道區別。它必須處於編譯器級別。 :/ –