Scala中的Currying示例

时间:2013-07-21 03:11:11

标签: scala currying

以下是currying的一个很好的例子吗?

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

3 个答案:

答案 0 :(得分:7)

在Scala中引入了Currying机制以支持类型推断。例如标准库中的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)

编写curried函数有三种方法:

1)以currying形式书写:

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

关于限制的更完整答案是{{3>} 反义词

答案 1 :(得分:4)

在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

因此,如果无法访问允许您定义两个参数的函数的语法,您只需使用函数sum获取参数a,返回等效于{{1}的函数接受参数adda并返回b。这就是所谓的 currying

作为练习,使用currying定义一个函数,可以让你处理3个参数。例如a + b,并填写问号中的内容。

答案 2 :(得分:1)

免责声明:我对Scala很新,所以要用一粒盐来对待

在纯函数式语言中,如Haskell currying在函数组合中起着非常重要的作用,例如:如果我想找到我会在Haskell中写的正方形的总和(抱歉Haskell太多了,但是语法与Scala有相似之处并且它并不难猜测)

没有说谎:

sum_of_squares xs = foldl (\x y -> x + y) 0 (map (\x -> x * x) xs)
带有限制的

.是一个函数组合):

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是OO语言,因此通常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 => Int

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

此表单对val sum2 = sum(2)无效,但您必须写val sum2 = sum(2) _