【问题标题】:Currying Example in ScalaScala 中的柯里化示例
【发布时间】:2013-07-20 01:45:06
【问题描述】:

以下是柯里化的一个很好的例子吗?

def sum(a: Int, b: Int) : (Int => Int) = {
    def go(a: Int) : Int = {
        a + b;
    }
    go
}

我对下面的结果理解了一半,但我怎么能以咖喱的方式写(或者我应该怎么写)sum()

scala> sum(3,4) res0: Int => Int = <function1>
scala> sum(3,4).apply(2) res1: Int = 6
scala> sum(3,4).apply(3) res2: Int = 7

【问题讨论】:

    标签: scala currying


    【解决方案1】:

    Scala 中引入了柯里化机制来支持类型推断。例如标准库中的foldLeft函数:

    def foldLeft[B](z: B)(op: (B, A) => B): B
    

    如果不使用currying,您必须明确提供类型:

    def foldLeft[B](z: B, op: (B, A) => B): B
    List("").foldLeft(0, (b: Int, a: String) => a + b.length)
    List("").foldLeft[Int](0, _ + _.length)
    

    柯里化函数的三种写法:

    1) 写成柯里化形式:

    def sum(a: Int)(b: Int) = a + b
    

    这只是语法糖:

    def sum(a: Int): Int => Int = b => a + b
    

    2) 在函数对象(sum _).curried 上调用curried 并检查类型:

    sum: (a: Int, b: Int)Int
    res10: Int => (Int => Int) = <function1>
    

    在您的示例中,您可以使用 Scala 类型推断来减少代码量并更改您的代码:

    def sum(a: Int, b: Int) : (Int => Int) = {
        def go(a: Int) : Int = {
            a + b;
        }
        go
    }
    

    进入:

    def sum(a: Int, b: Int) : (Int => Int) = c => a + b + c
    

    语义上它们是相同的,因为您明确提供了返回类型,所以 Scala 知道您将返回一个函数,该函数接受一个 Int 参数并返回一个 Int

    retronym

    given 也是关于 curring 的更完整答案

    【讨论】:

    • 严格来说def sum_1(a: Int)(b: Int) = a + b 不是def sum_2(a: Int): Int =&gt; Int = b =&gt; a + b 的语法糖,因为我可以调用sum_2(1),但我不能调用sum_1(1)(我将不得不调用sum_1(1) _)。 sum_1 不被视为与柯里化概念相矛盾的单参数方法。
    • 从声明中 'def sum(a: Int): Int => Int = b => a + b' 'b' 是从哪里来的?
    • @4lex1v 我知道这是一个旧答案,但您对def sum(a: Int, b: Int) : (Int =&gt; Int) = c =&gt; a + b + c 的答案的最后一个示例中的结果是 9,因此不等同于问题
    【解决方案2】:

    在 lambda 演算中,您有一个称为 lambda 抽象 λx.term1 的东西,当应用于另一个术语 (λx.term1)(term2) 时,对应于将函数应用于 term2 的概念。 lambda演算是函数式编程的理论基础。在 lambda 演算中,您没有采用多个参数的 lambda 抽象。那么你如何表示两个参数的函数呢?答案是返回一个接受另一个参数的函数,然后返回两个参数的结果。

    所以在 Scala 中,如果你在作用域中有一个 var a,你可以返回一个函数,它将其参数 b 添加到 a

    scala> var a = 1
    a: Int = 1
    
    scala> val adda = (b: Int) => a + b
    adda: Int => Int = <function1>
    
    scala> adda(3)
    res1: Int = 4
    

    现在,如果您在范围内有一个参数 a,它也可以正常工作:

    scala> val sum = (a: Int) => (b: Int) => a + b
    sum: Int => Int => Int = <function1>
    
    scala> sum(3)(5)
    res2: Int = 8
    

    因此,如果无法访问允许您定义两个参数的函数的语法,您基本上只需使用带有参数 a 的函数来实现这一点987654333@ 并返回a + b。这就是所谓的currying

    作为练习,使用柯里化定义一个函数,让您处理 3 个参数。比如val sum3: Int =&gt; Int =&gt; Int =&gt; Int = ???,问号里面填什么。

    【讨论】:

      【解决方案3】:

      免责声明:我对 Scala 还很陌生,所以请谨慎对待

      在像 Haskell 这样的纯函数式语言中,柯里化在函数组合中起着非常重要的作用,例如如果我想求平方和,我会用 Haskell 编写(对不起,Haskell 太多了,但语法与 Scala 有相似之处,并不难猜)

      没有柯里化:

      sum_of_squares xs = foldl (\x y -> x + y) 0 (map (\x -> x * x) xs)
      

      带有curring(.是一个函数组合):

      sum_of_squares = (foldl (\x y -> x + y) 0) . (map (\x -> x * x))
      

      这允许我使用函数而不是使用参数进行操作。前面的例子可能不太清楚,但考虑一下:

      sum_of_anything f = (foldl (\x y -> x + y) 0) . (map f)
      

      这里f 是一个任意函数,我可以将第一个示例重写为:

      sum_of_squares = sum_of_anything (\x -> x * x)
      

      现在让我们回到 Scala。 Scala 是面向对象的语言,所以通常xs 会是一个接收者:

      def sum_of_squares(xs: List[Int]): Int = {
        xs.map(x => x * x).foldLeft(0)((x, y) => x + y)
      }
      
      sum_of_squares(List(1,2,3))
      
      def sum_of_anything(f: (Int, Int) => Int)(xs: List[Int]): Int = {
        xs.map(x => x * x).foldLeft(0)(f)
      }
      
      sum_of_anything((x, y) => x + y)(List(1, 2, 3))
      

      这意味着我不能省略xs。我可能可以用 lambdas 重写它,但如果不添加更多样板,我将无法使用 mapfoldLeft。因此,正如其他人在 Scala 中提到的“currying”可能主要用于支持类型推断。

      同时,在您的特定示例中,我感觉您不需要外部 a,无论如何它都被遮住了,您可能的意思是:

      def sum(b: Int) : (Int => Int) = {
          def go(a: Int) : Int = {
              a + b;
          }
          go
      }
      

      但在这个简单的示例中,您可以使用部分应用程序(假设您可能会将sum 传递给高阶函数):

      List(1, 2, 3).map(sum(2))   //> res0: List[Int] = List(3, 4, 5)
      List(1, 2, 3).map(_ + 2)    //> res1: List[Int] = List(3, 4, 5)
      

      对于这种应用程序,sum 可以更短,因为sum(2) 将隐式扩展为Int =&gt; Int

      def sum(b: Int)(a: Int): Int = a + b
      

      此表格不适用于val sum2 = sum(2),但您必须填写val sum2 = sum(2) _

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-08-17
        • 2018-01-24
        • 2021-02-07
        • 1970-01-01
        • 2021-03-11
        • 2011-11-03
        相关资源
        最近更新 更多