以下是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
答案 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重写它,但如果不添加更多样板,我将无法使用map
和foldLeft
。因此,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) _
。