斯卡拉咖喱与部分应用的function

对不起,新手问题。 我意识到这里有几个关于什么是咖喱和部分应用function的问题,但是我在问他们是如何不同的。 作为一个简单的例子,这里是一个查找偶数的curry函数:

def filter(xs: List[Int], p: Int => Boolean): List[Int] = if (xs.isEmpty) xs else if (p(xs.head)) xs.head :: filter(xs.tail, p) else filter(xs.tail, p) def modN(n: Int)(x: Int) = ((x % n) == 0) 

所以你可以写下面的内容来使用它:

 val nums = List(1,2,3,4,5,6,7,8) println(filter(nums, modN(2)) 

它返回: List(2,4,6,8) 。 但我发现我可以这样做同样的事情:

 def modN(n: Int, x: Int) = ((x % n) == 0) val p = modN(2, _: Int) println(filter(nums, p)) 

这也返回: List(2,4,6,8)

所以我的问题是,这两者之间的主要区别是什么,你们什么时候使用一个呢? 这只是一个例子,太简单了,以显示为什么会被用于另一个? 非常感谢你的帮助。

在Plasty Grove的答案中 ,语义差异已经得到很好的解释。

在function方面,似乎没有太大的差别。 我们来看一些例子来validation。 首先,一个正常的function:

 scala> def modN(n: Int, x: Int) = ((x % n) == 0) scala> modN(5, _ : Int) res0: Int => Boolean = <function1> 

所以我们得到一个部分应用的<function1> ,它接受一个Int ,因为我们已经给它第一个整数。 到现在为止还挺好。 现在来currying:

 scala> def modNCurried(n: Int)(x: Int) = ((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) 

所以多参数列表符号似乎并不真正创build一个curried函数(假设是为了避免不必要的开销),而是等待你明确声明你想要curried(该符号还有其他一些优点 ):

 scala> modNCurried(5) _ res24: Int => Boolean = <function1> 

这与我们之前所得到的完全一样,除了符号之外,这里没有什么区别。 另一个例子:

 scala> modN _ res35: (Int, Int) => Boolean = <function2> scala> modNCurried _ res36: Int => (Int => Boolean) = <function1> 

这演示了如何部分应用“正常”函数产生一个采用所有参数的函数,而部分应用具有多个参数列表的函数会创build一个函数链, 每个参数列表一个函数,它们都会返回一个新函数:

 scala> def foo(a:Int, b:Int)(x:Int)(y: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的第一个参数列表有两个参数,curried链中的第一个函数有两个参数。


总之,就function而言,部分应用的function并不是完全不同的。 这很容易validation,因为你可以将任何函数转换为咖喱types:

 scala> (modN _).curried res45: Int => (Int => Boolean) = <function1 scala> modNCurried _ res46: Int => (Int => Boolean) = <function1> 

发布Scriptum

注意:您的示例println(filter(nums, modN(2))modN(2)之后没有下划线的原因似乎是Scala编译器只是简单地假定下划线是程序员的便利。


另外:正如@asflierl已经正确指出的,当部分应用“正常”函数时,Scala似乎不能推断出types:

 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> 

这个答案显示了这可能是非常有用的。

Currying与元组有关: 将一个将tuple参数转换为一个需要n个独立参数的函数,反之亦然 。 记住这一点是区分咖喱和部分应用的关键,即使是不能干净地支持咖喱的语言。

 curry :: ((a, b) -> c) -> a -> b -> c -- curry converts a function that takes all args in a tuple -- into one that takes separate arguments uncurry :: (a -> b -> c) -> (a, b) -> c -- uncurry converts a function of separate args into a function on pairs. 

部分应用是对一些参数应用一个函数的能力, 为剩下的参数产生一个新的函数

很容易记住,如果你只是觉得咖喱是对元组的转换。

在默认情况下curried的语言(如Haskell)中,区别很明显 – 您必须实际做一些事情来传递元组中的参数。 但包括Scala在内的大多数其他语言默认情况下不会出错 – 所有的参数都是以元组的forms传递的,所以咖喱/咖喱没有什么用处,也不太明显。 甚至人们甚至认为部分应用和咖喱是同一件事 – 仅仅因为它们不能容易地表示咖喱的function!

多variables函数:

 def modN(n: Int, x: Int) = ((x % n) == 0) 

柯里(或咖喱function):

 def modNCurried(n: Int)(x: Int) = ((x % n) == 0) 

所以它不是部分应用的function,可与咖喱相媲美。 这是多variables函数。 与部分应用的函数相比,curr函数的调用结果是一个具有与部分应用的函数相同的参数列表的函数。

只是为了澄清最后一点

另外:正如@a​​sflierl已经正确指出的,当部分应用“正常”函数时,Scala似乎不能推断出types:

如果所有参数都是通配符,Scala可以推断types,但是当它们中的一些被指定时,Scala可以推断types。

 scala> modN(_,_) res38: (Int, Int) => Boolean = <function2> scala> modN(1,_) <console>:13: error: missing parameter type for expanded function ((x$1) => modN(1, x$1)) modN(1,_) ^