2011-08-23 50 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

是的,然后我只是困惑如何斯卡拉知道区别。它必须处于编译器级别。 :/ –