2013-10-20 22 views
22

Dеar斯卡拉,Scala中的“case”匿名函数是如何工作的?

scala> val f1: ((Int, Int)) => Int = { case (a, b) => a + b } 
f1: ((Int, Int)) => Int = <function1> 

scala> val f2: (Int, Int) => Int = { case (a, b) => a + b } 
f2: (Int, Int) => Int = <function2> 

吧?

scala> f1(1, 2) 
res2: Int = 3 

好吧......

scala> def takesIntInt2Int(fun: (Int, Int) => Int) = fun(100, 200) 
takesIntInt2Int: (fun: (Int, Int) => Int)Int 

scala> def takesTuple2Int(fun: ((Int, Int)) => Int) = fun(100, 200) 
takesTuple2Int: (fun: ((Int, Int)) => Int)Int 

scala> takesIntInt2Int(f2) 
res4: Int = 300 

scala> takesIntInt2Int(f1) 
<console>:10: error: type mismatch; 
found : ((Int, Int)) => Int 
required: (Int, Int) => Int 
       takesIntInt2Int(f1) 
          ^

scala> takesTuple2Int(f1) 
res6: Int = 300 

scala> takesTuple2Int(f2) 
<console>:10: error: type mismatch; 
found : (Int, Int) => Int 
required: ((Int, Int)) => Int 
       takesTuple2Int(f2) 

权。现在,看看这个!

scala> takesTuple2Int { case (a, b, c) => a + b + c } 
<console>:9: error: constructor cannot be instantiated to expected type; 
found : (T1, T2, T3) 
required: (Int, Int) 
       takesTuple2Int { case (a, b, c) => a + b + c } 
            ^

scala> takesIntInt2Int { case (a, b, c) => a + b + c } 
<console>:9: error: constructor cannot be instantiated to expected type; 
found : (T1, T2, T3) 
required: (Int, Int) 
       takesIntInt2Int { case (a, b, c) => a + b + c } 

一样,srsly? o_O两者都导致required: (Int, Int)错误。

为什么在这样的匿名函数中完全可以使用case

回答

15

请参阅Scala参考资料的第8.5节(http://www.scala-lang.org/files/archive/nightly/pdfs/ScalaReference.pdf)。基于预期类型,表达式{ case (a, b) => a + b }的解释是不同的。在f1的定义中,它创建了PartialFunction[(Int, Int), Int],该PartialFunction[(Int, Int), Int]投射到Function1[(Int, Int), Int],即((Int, Int)) => Int,而在f2的定义中,它创建了Function2[Int, Int, Int],即(Int, Int) => Int

这两种解释涉及您通常在匿名函数中使用大小写的两种情况。

一个用于编写接受元组并处理其组件的匿名函数,就像您使用f1所做的那样。一个例子是您传递给Map上的foreachmap方法的功能,例如, Map(1 -> 2, 3 -> 4) map { case (k, v) => k + v }

第二个是写一个匿名函数,它的唯一参数是做一个match。你的f2正在这样做,但没有任何有用的方式。一个例子是传递给collect的匿名函数,例如List(1, -2, 3) collect { case x if x > 0 => -x }

请注意,两者可以组合,也就是说像f1这样的功能也可以做复杂的匹配。例如,Map(1 -> 2, 3 -> 4) collect { case (k, v) if k < 2 => v }

编辑:res2工程因为tupling。如果应用程序没有键入check,编译器会尝试在发生失败之前将参数包装在一个元组中。

但是,这只是应用程序的尝试;正如你发现的那样,这不是一般的转换。它不会尝试将值Function2[A, B, C]更新为Function1[(A, B), C]

+1

能够在不使用'case'关键字的情况下做到这一切真是太棒了。为什么从正常开发人员的角度来看Function和PartialFunction的语法差异? –

+2

@MichałRus说实话,这总是让我有点困扰。 Haskell和Clojure具有更简单的语法来直接在函数的参数上表示模式匹配。 – wingedsubmariner