currying和多个参数列表之间有什么区别?

时间:2013-11-11 20:48:43

标签: scala currying

我看到的每个地方,我看到多个参数列表和currying用于互换的术语。我在几十个stackoverflow问题中看到它,甚至在scala-lang.org上。例如,This page的标题为“Currying”。而第一句话? “方法可以定义多个参数列表。”

然而,一些知识渊博的人在看到多个参数列表并且等同于讨论时会感到恼火。我发布了this question的答案,但是当我看到Randall Schulz的评论时我将其删除了,因为我担心我可能无意中散布了错误的信息。我的理解是,具有多个参数列表的函数必然是一个curried函数,但函数currying也可以通过其他方式实现(this question的最佳答案列出四种方式),但我不确定是整个故事。我想真正理解这种区别。

我知道在stackoverflow上有很多非常类似的问题,但是我没有找到一个精确来解释差异的问题。关于多个参数列表和currying,我需要了解什么才能准确地说出它们?

1 个答案:

答案 0 :(得分:23)

我希望你不介意我是否从Haskell示例开始,因为Haskell是一种比Scala简单得多的语言。在Haskell中,所有函数都是数学意义上的函数 - 它们接受一个参数并返回一个值。 Haskell也有元组,你可以编写一个看起来的函数,就像它需要多个参数一样,作为从元组到任何函数的函数。例如:

Prelude> let add = (\(x, y) -> x + y) :: (Int, Int) -> Int
Prelude> add (1, 2)
3

现在我们可以 curry 这个函数来获取类型Int -> Int -> Int而不是(Int, Int) -> Int的函数:

Prelude> let curriedAdd = curry add

这允许我们部分应用该功能,例如:

Prelude> let add3 = curriedAdd 3
Prelude> add3 1
4

所以我们有一个很好的干净定义currying - 它是一个函数,它接受一个参数的元组(特别是一对)的函数,并返回一个函数,该函数将该对中的第一个类型作为参数并返回一个函数从该对中的第二种类型到原始返回类型。这只是一个冗长的说法:

Prelude> :t curry
curry :: ((a, b) -> c) -> a -> b -> c

好的,现在是Scala。

在Scala中,您还可以使用带有元组参数的函数。 Scala还具有多个参数(Function2及以上)的“函数”。这些是(令人困惑的)不同种类的动物。

Scala还有一些方法,它们与函数不同(尽管它们可以通过 eta扩展或多或少地自动转换为函数)。方法可以有多个参数,或元组参数,或多个参数列表。

那么说我们在这种背景下讨论一些事情意味着什么呢?

从字面上看,currying是你使用Function2(及以上)来做的事情:

scala> val add: Function2[Int, Int, Int] = (x: Int, y: Int) => x + y
add: (Int, Int) => Int = <function2>

scala> val curriedAdd = add.curried
curriedAdd: Int => (Int => Int) = <function1>

scala> val add3 = curriedAdd(3)
add3: Int => Int = <function1>

这与我们在Haskell案例中看到的大致相同,只是我们正在讨论具有多个参数的函数,这在Haskell中是不正确存在的。

现在我非常确定这是 curry 这个词实际出现在Scala标准库中的唯一背景(不包括Function companion object上的uncurried )但是考虑到Scala在方法,功能等方面的巨大混乱(不要误解我 - 我喜欢Scala,但这部分语言是彻底的灾难),对我来说似乎很合理在以下背景中应用该词:

def add(x: Int, y: Int) = x + y
def curriedAdd(x: Int)(y: Int) = add(x, y)

这里我们改变了一个方法,它将两个参数带入一个带有多个参数列表的方法 - ,每个参数列表只需要一个参数(最后一部分很重要)。

事实上,language specification也在此上下文中使用该术语,将以下内容描述为“单个,curried函数定义”:

def func(x: Int)
        (y: Int) = x + y

(这当然令人困惑,因为这是一种方法,而不是一种功能。)

总而言之:多个参数列表是在Scala中实现currying的一种方法,但并非所有具有多个参数列表的方法都是curried-only,其中每个参数列表都有一个参数。无论如何,所有的术语都非常糊涂,所以不要过于担心它的正确性。