18

在我所见的任何地方,我都能看到术语多参数列表和可互换使用的柯里化。我在几十个 stackoverflow 问题中看到了它,甚至在 scala-lang.org 上也看到了它。 例如,此页面的标题为“Currying”。第一句话呢?“方法可以定义多个参数列表。”

然而,当一些知识渊博的人看到多个参数列表和柯里化等同时,他们会感到恼火。我发布了这个问题的答案,但是当我看到 Randall Schulz 的评论时将其删除,因为我担心我可能会无意中传播错误信息。我的理解是,具有多个参数列表的函数必然是柯里化函数,但函数柯里化也可以通过其他方式实现(该问题的最佳答案列出了四种方式),但我不确定这就是全部故事. 我想真正了解这种区别。

我知道在 stackoverflow 上有很多与这个问题非常相似的问题,但我还没有找到一个能准确说明差异的问题。为了准确地谈论它们,我需要了解多个参数列表和柯里化什么?

4

1 回答 1

24

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

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

现在我们可以对这个函数进行curryInt -> Int -> Int来得到一个 type 的函数,而不是(Int, Int) -> Int:

Prelude> let curriedAdd = curry add

这使我们可以部分应用该功能,例如:

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

所以我们有一个很好的柯里化定义——它是一个函数,它接受一个带有元组(特别是一对)的函数作为参数,并返回一个函数,该函数将对中的第一个类型作为参数,并从第二个类型返回一个函数在对中输入原始返回类型。这只是一种罗嗦的说法:

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

好的,现在是 Scala。

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

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

那么在这种情况下说我们正在咖喱什么是什么意思呢?

从字面上看,currying 是你用 a Function2(和 up) 做的事情:

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 标准库中的唯一上下文(不包括伴随对象uncurried上的Function伴随对象),但是考虑到 Scala 对方法、函数、等等(不要误会我的意思——我喜欢 Scala,但是这部分语言完全是一场灾难),对我来说,在以下上下文中应用这个词似乎也很合理:

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

在这里,我们将一个接受两个参数的方法变成了一个具有多个参数列表的方法——<em>每个参数列表只接受一个参数(最后一部分很重要)。

事实上,语言规范也在这种情况下使用了该术语,将以下描述为“单一的柯里化函数定义”:

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

(这当然令人困惑,因为这是一个方法,而不是一个函数。)

总结一下:多参数列表是在 Scala 中实现柯里化的一种方法,但并非所有具有多个参数列表的方法都被柯里化——只有每个参数列表都有一个参数的方法。无论如何,所有的术语都很糊涂,所以不要太担心它是否正确。

于 2013-11-11T21:36:55.917 回答