2013-01-13 38 views
76

我知道这里有几个问题关于什么柯里里和部分应用的功能,但我问他们是如何不同。作为一个简单的例子,这里是一个咖喱功能查找连号:斯卡拉柯里与部分应用的功能

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)

所以我的问题是,这两者之间的主要区别是什么,以及什么时候使用两者之一?这是否仅仅是一个例子的过分简单来说明为什么一个人会被用于另一个?

+0

部分应用时,在内存中表示curried和non-curried版本的代价可能会不同,因此运行时性能可能也会受到影响。 (也就是说,如果优化器在两种情况下都不够聪明,无法选择最佳表示),但我不太熟悉Scala说明确切的区别。 – 2013-01-13 23:41:33

+1

看看这个:http://stackoverflow.com/questions/8063325/usefulness-as-in-practical-applications-of-currying-vs-partial-application-i – Utaal

+0

我发现这个解释非常有用,他解释了部分功能,部分应用的功能和咖喱,所有在一个职位:http://stackoverflow.com/a/8650639/1287554 –

回答

83

the answer linked to by Plasty Grove中已经很好地解释了语义差异。

在功能方面,似乎没有太大区别。让我们看看一些例子来验证。首先,正常功能:

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

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

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) 

所以多个参数列表符号并没有真正似乎创造咖喱功能(假设为了避免不必要的开销),但等待你明确说明你想要它curried(该符号也有一些other advantages以及):

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) = 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> 

邮政Scriptum

注:的是你的榜样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> 

This answers显示这可以如何有用。

+0

一个重要的区别是类型推断的工作方式不同:https://gist.github.com/4529020 – 2013-01-14 09:57:03

+0

谢谢,我添加了关于您的评论的说明:) – fresskoma

17

Currying是用元组处理的:将一个把元组参数转换为一个带有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,默认情况下都不会出错 - 所有的参数都是以元组的形式传递的,所以咖喱/不结块的用处就不那么明显了。人们甚至认为部分应用和咖喱是同一件事 - 仅仅是因为他们不能轻松表示咖喱的功能!

+0

我完全同意。在Scala中,术语“currying”的意思是将具有一个参数列表的函数转换为具有多个参数列表的函数。在Scala中,这个转换可以使用“.curried”来执行。不幸的是,斯卡拉似乎已经超出了这个词的含义,因为它最初被称为“.curry”而不是“.curried”。 –

2

多变量函数:

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

柯里(或咖喱功能):

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

,所以它不是部分应用功能这比得上讨好。这是多变量函数。 与部分应用函数相媲美的是curried函数的调用结果,该函数是具有与部分应用函数具有相同参数列表的函数。

0

只是为了在最后一点上澄清

增加:由于@asflierl已经正确地指出,斯卡拉似乎 并不能够推断出类型时,部分应用的“正常” 功能:

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

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,_) 
      ^