语义差异已经得到很好的解释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)
So the 多参数列表符号似乎并没有立即创建一个柯里化函数(假设是为了避免不必要的开销),而是等待您明确声明您希望它柯里化(该符号有一些其他优点以及):
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>
后记
Note:你的例子的原因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>
这个答案展示了这如何非常有用。