Scala中的Currying示例

Scala中的Currying示例,scala,currying,Scala,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) r

下面是一个好的咖喱例子吗

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>sum(3,4)res0:Int=>Int=
scala>sum(3,4)。应用(2)res1:Int=6
scala>sum(3,4)。应用(3)res2:Int=7

在Scala中引入了curry机制以支持类型推断。例如,标准库中的
foldLeft
函数:

def foldLeft[B](z: B)(op: (B, A) => B): B
如果不使用curry,则必须显式提供类型:

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) 用咖喱书写:

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知道您将返回一个函数,其中包含一个
Int
参数并返回一个
Int

关于咖喱的另一个更完整的答案是:我对Scala还很陌生,所以请少说几句

在像Haskell这样的纯函数语言中,currying在函数组合中扮演着非常重要的角色,例如,如果我想找到平方和,我会用Haskell编写(很抱歉用了太多Haskell,但语法与Scala有相似之处,这并不难猜测)

不使用咖喱:

sum_of_squares xs = foldl (\x y -> x + y) 0 (map (\x -> x * x) xs)
使用curring(
是一种函数组合):

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

sum_of_anything f = (foldl (\x y -> x + y) 0) . (map f)
这里的
f
是一个任意函数,我可以将第一个示例改写为:

sum_of_squares = sum_of_anything (\x -> x * x)
现在让我们回到斯卡拉。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
传递给高阶函数):

对于此类应用程序,
sum
可以更短,因为
sum(2)
将隐式扩展为
Int=>Int

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

此形式对
val sum2=sum(2)
无效,但是,您必须在lambda演算中编写
val sum2=sum(2)
,您有一个称为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
因此,不需要访问允许定义两个参数的函数的语法,只需使用函数
sum
获取参数
a
返回一个等价于
adda
的函数,该函数获取参数
b
并返回
a+b
。这就是所谓的咖喱


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

严格地说
def-sum\u 1(a:Int)(b:Int)=a+b
不是
def-sum\u 2(a:Int):Int=>Int=b=>a+b的语法糖,我可以称之为
sum\u 2(1)
,但我不能称之为
sum\u 1(1)
(我将不得不调用
sum_1(1)_
sum_1
不被视为与curry的概念相矛盾的一个参数方法。从声明的“def sum(a:Int)”:Int=>Int=b=>a+b'b'从何而来?@4lex1v我知道这是一个古老的答案,但您对
def sum(a:Int,b:Int)的最后一个答案示例的结果是:(Int=>Int)=c=>a+b+c
是9,因此不是问题的等价物
def sum(b: Int) : (Int => Int) = {
    def go(a: Int) : Int = {
        a + b;
    }
    go
}
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)
def sum(b: Int)(a: Int): Int = a + b
scala> var a = 1
a: Int = 1

scala> val adda = (b: Int) => a + b
adda: Int => Int = <function1>

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

scala> sum(3)(5)
res2: Int = 8