15

我正在了解有关 Scala 的更多信息,并且在理解http://www.scala-lang.org/node/135中的匿名函数示例时遇到了一些麻烦。我复制了下面的整个代码块:

object CurryTest extends Application {
    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)))
    println(filter(nums, modN(3)))
}

我对 modN 函数的应用感到困惑

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

在示例中,它使用一个参数调用

modN(2) and modN(3)

modN(n: Int)(x: Int) 的语法是什么意思?

由于它是用一个参数调用的,我假设它们不是两个参数,但我无法真正弄清楚 mod 函数如何使用来自 nums 的值。

4

3 回答 3

47

这是函数式编程中一个有趣的东西,叫做currying。基本上,Moses Schönfinkel 和后来的 Haskell Curry(虽然 Schonfinkeling 听起来很奇怪......)提出了这样的想法,即调用多个参数的函数,比如说f(x,y)与调用链相同,{g(x)}(y)或者g(x)(y)在哪里g产生另一个函数作为它的函数输出。

以函数为例f(x: Int, y: Int) = x + y。正如预期的那样,调用f(2,3)将产生。5但是当我们对这个函数进行 curry 时会发生什么——将它重新定义为f(x:Int)(y: Int)并将其称为f(2)(3). 第一次调用,f(2)产生一个接受一个整数y并添加2到它的函数 -> 因此f(2)具有类型Int => Int并且等价于 function g(y) = 2 + y。第二次调用使用参数f(2)(3)调用新生成的函数,因此按预期计算为 。g35

另一种查看它的方法是逐步执行调用的缩减(函数式程序员称之为 beta-reduction - 它就像逐行步进的函数式方式)f(2)(3)(注意,以下不是真正有效的 Scala 语法)。

f(2)(3)         // Same as x => {y => x + y}
 | 
{y => 2 + y}(3) // The x in f gets replaced by 2
       |
     2 + 3      // The y gets replaced by 3
       |
       5

因此,经过所有这些讨论,f(x)(y)可以将其视为以下 lambda 表达式(x: Int) => {(y: Int) => x + y}——它是有效的 Scala。

我希望这一切都是有意义的——我试图给出一些背景说明为什么这个modN(3)电话是有意义的:)

于 2009-10-11T15:08:26.180 回答
3

您正在部分应用 ModN 功能。偏函数应用是函数式语言的主要特点之一。有关更多信息,请查看有关CurryingPointfree样式的这些文章。

于 2009-10-11T15:01:40.890 回答
2

在该示例中, modN返回一个由特定 N 修改的函数。它使您不必这样做:

def mod2(x:Int): Boolean = (x%2) == 0
def mod3(x:Int): Boolean = (x%3) == 0

两对括号界定了您可以停止将参数传递给方法的位置。当然,即使方法只有一个参数列表,您也可以只使用占位符来实现相同的目的。

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

val nums = List(1, 2, 3, 4, 5)
println(nums.filter(modN(2, _)))
println(nums.filter(modN(3, _)))
于 2009-10-11T14:53:04.060 回答