4

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

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
4

3 回答 3

8

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)调用curried函数对象(sum _).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

retroonym也给出了关于 curring 的更完整的答案

于 2013-07-21T04:39:32.850 回答
4

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

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

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

因此,如果无法访问允许您定义两个参数的函数的语法,您基本上只需通过一个sum带参数a的函数返回一个等效于adda带一个参数b并返回的函数来实现这一点a + b。这就是所谓的柯里化

作为一个练习,使用 currying 定义一个函数,它可以让您处理 3 个参数。例如val sum3: Int => Int => Int => Int = ???,并填写问号中的内容。

于 2013-07-21T17:19:37.547 回答
1

免责声明:我对 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 重写它,但是如果不添加更多样板map,我将无法使用。foldLeft因此,正如其他人在 Scala 中提到的“currying”可能主要用于支持类型推断。

同时,在您的特定示例中,我感觉您不需要 outer 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 => Int

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

但是,此表格无效val sum2 = sum(2),您必须写val sum2 = sum(2) _.

于 2013-07-21T15:24:21.263 回答