【问题标题】:Scala Syntax Help CurryingScala 语法帮助 Currying
【发布时间】:2014-06-06 16:18:09
【问题描述】:

我在 scala 中遇到了一些类似形式的代码:

  def test1(f : Int => Int)(x : Int) = x + f(x)

  def test2(f : Int => Int)(x : Int) = f(x)

  test2(test1(x => 2*x))(2)

我很困惑,所以函数 test1 将一个函数和一个 Int 作为参数,并返回一个函数,对吗?那么test1(x => 2*x)如何才能有效并返回一个函数给test2呢?显然它需要 2 作为整数参数,但为什么呢? test2(test1(x => 2*x))(2)这个语句是怎么展开的?

提前致谢。

【问题讨论】:

    标签: scala functional-programming currying


    【解决方案1】:

    function test1 接受一个函数和一个 Int 作为参数,并返回一个函数,对吧?

    不,它只接受一个函数作为参数并返回一个函数。然后返回的函数将一个 int 作为参数并返回一个 int。

    那么 test1(x => 2*x) 怎么才能有效并返回一个函数给 test2 呢?

    我希望现在清楚了。

    语句 test2(test1(x => 2*x))(2) 如何展开?

    test1x => 2*x 作为参数调用并返回一个函数。然后test2 以返回的函数作为参数调用并返回另一个函数。然后以2 作为参数调用另一个函数。

    【讨论】:

    • 谢谢!我如此沉迷于命令式编程风格,认为柯里化只是一种语法糖。这现在很有意义。
    • 这不太正确。在 REPL 中单独尝试 test1(x => 2*x)
    【解决方案2】:

    这个:

    test2(test1(x => 2*x))(2)
    

    扩展为:

    test2(y => test1(x => 2*x)(y))(2)
    

    Scala 的具有多个参数列表的方法的行为很像其他语言中的柯里化函数,但实际上是作为需要所有参数的方法实现的,这在语法中确实体现出来了。例如,如果您将其放入 REPL:

    test1(x => 2*x)
    

    它会抱怨缺少参数。这种语法确实允许的是“eta-expansion”,其中方法被转换为函数。如果你写:

    val func: Int => Int = test1(x => 2*x) _
    

    您可以得到一个表示test1 部分应用的函数。如果上下文需要,Scala 会自动进行 eta-expansion,test2(test1(x => 2*x))(2) 就是这样。请注意,test1 本身从不返回函数,但编译器会在需要时基于它构建函数。

    但是,如果您将test1 定义为:

    def test1(f : Int => Int) = (x : Int) => x + f(x)
    

    然后编译器将接受 test1(x => 2*x) 而不接受 _。此外,在底层实现中,只有一个类实现由test1 返回的闭包,而在原始定义中,test1 的每个部分应用程序都会为其创建一个新类。另一方面,同时给定两个参数时效率较低,因为始终会创建闭包。

    【讨论】:

    • 好的,我可以说Scala在这种情况下使用eta-expansion来实现函数柯里化,但它鼓励我们认为test1返回一个函数?
    • 有点。有很多文档建议多个参数列表实现柯里化,但你不能像真正的柯里化一样盲目地使用它,而且大多数具有多个参数列表的方法实例并不意味着部分应用,例如list.foldLeft(0) 很少见。查看我的新编辑 - 我展示了如何创建真正的柯里化方法/函数。
    猜你喜欢
    • 1970-01-01
    • 2021-06-10
    • 2016-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多