在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>
这个答案显示了这如何非常有用。