普拉斯特·格罗夫(Plasty Grove)链接的答案已经很好地解释了语义差异。
在功能方面,似乎没有太大的区别。让我们看一些例子来验证这一点。一,正常功能:
scala> def modN(n: Int, x: Int): Boolean = ((x % n) == 0)
scala> modN(5, _ : Int)
res0: Int => Boolean = <function1>
因此,我们得到了一个部分<function1>
接受的Int
,因为我们已经给了它第一个整数。到现在为止还挺好。现在开始:
scala> def modNCurried(n: Int)(x: Int): Boolean = ((x % n) == 0)
使用这种表示法,您会天真地期待以下工作:
scala> modNCurried(5)
<console>:9: error: missing arguments for method modN;
follow this method with `_' if you want to treat it as a partially applied function
modNCurried(5)
因此,多参数列表表示法似乎并没有立即创建一个咖喱函数(以免造成不必要的开销),而是等待您明确声明您希望咖喱函数(这种记法还具有其他一些优点):
scala> modNCurried(5) _
res24: Int => Boolean = <function1>
这与我们之前得到的完全一样,因此在这里除了符号没有区别。另一个例子:
scala> modN _
res35: (Int, Int) => Boolean = <function2>
scala> modNCurried _
res36: Int => (Int => Boolean) = <function1>
这说明了部分应用“普通”函数如何导致具有所有参数的函数,而部分应用具有多个参数列表的函数会创建一系列函数,每个参数列表一个,都返回一个新函数:
scala> def foo(a:Int, b:Int)(x:Int)(y:Int): Int = a * b + x - y
scala> foo _
res42: (Int, Int) => Int => (Int => Int) = <function2>
scala> res42(5)
<console>:10: error: not enough arguments for method apply: (v1: Int, v2: Int)Int => (Int => Int) in trait Function2.
Unspecified value parameter v2.
如您所见,由于的第一个参数列表foo
具有两个参数,因此咖喱链中的第一个功能具有两个参数。
总而言之,就功能而言,部分应用的函数在形式上并没有什么不同。鉴于您可以将任何函数转换为咖喱函数,这很容易验证:
scala> (modN _).curried
res45: Int => (Int => Boolean) = <function1
scala> modNCurried _
res46: Int => (Int => Boolean) = <function1>
圣经后
注意:您的示例println(filter(nums, modN(2))
在之后没有下划线的情况下工作的原因modN(2)
似乎是因为Scala编译器只是假定下划线为程序员提供了便利。
另外:正如@asflierl正确指出的那样,当部分应用“正常”函数时,Scala似乎无法推断类型:
scala> modN(5, _)
<console>:9: error: missing parameter type for expanded function ((x$1) => modN(5, x$1))
而该信息可用于使用多个参数列表符号编写的函数:
scala> modNCurried(5) _
res3: Int => Boolean = <function1>
这个答案说明了它如何非常有用。