匿名 Scala 函数语法

Posted

技术标签:

【中文标题】匿名 Scala 函数语法【英文标题】:Anonymous Scala function syntax 【发布时间】:2009-10-11 14:31:08 【问题描述】:

我正在了解有关 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 的值。

【问题讨论】:

【参考方案1】:

这是函数式编程中的一件有趣的事情,称为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 并且等效于函数 g(y) = 2 + y。第二次调用 f(2)(3) 调用新生成的函数 g 并带有参数 3,因此按预期计算为 5

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

【讨论】:

很好的解释。我将您的答案更改为已接受的答案(加上您修复了上一个示例中的错误)。 赞成“Schonfinkeling”将参数转换为函数。从今以后我只用这个词。 :-D【参考方案2】:

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

【讨论】:

【参考方案3】:

在该示例中,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, _)))

【讨论】:

感谢您的详细回复。在带有占位符的第二个示例中,您能否解释最后一个 : Int 的用途,即: def modN(n: Int)(x: Int): Int vs def modN(n: Int)(x: Int) 是这样吗只是占位符可以使用和不能使用的语法区别? 另外,我刚刚用占位符 _ 尝试了第二个示例,编译器抱怨 modN 的参数数量错误。 那是因为modN(x, y) 不是调用函数的有效方式(Scala 不会自动取消currying - 即从f(x)(y) 转换为f(x, y))。因此,在示例中调用modN 的正确方法是modN(2)(_)。还有,小吹牛——modN的返回类型不对,应该是modN(n: Int)(x: Int): Boolean = (x % n) == 0。或者你可以让类型推断器推断它。 :) 我更新了第二个示例以减少错误。感谢弗拉维的解释。

以上是关于匿名 Scala 函数语法的主要内容,如果未能解决你的问题,请参考以下文章

Scala函数式编程进阶

寒假第五天

scala

Spark函数式编程进阶

scala quick check

scala学习笔记-函数式编程(14)