Scala 中柯里化的两种方式;每个用例是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4915027/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Two ways of currying in Scala; what's the use-case for each?
提问by davetron5000
I am having a discussion around Multiple Parameter Listsin the Scala Style Guide I maintain. I've come to realize that there are two ways of currying, and I'm wondering what the use cases are:
我正在讨论我维护的 Scala 样式指南中的多参数列表。我开始意识到有两种currying方式,我想知道用例是什么:
def add(a:Int)(b:Int) = {a + b}
// Works
add(5)(6)
// Doesn't compile
val f = add(5)
// Works
val f = add(5)_
f(10) // yields 15
def add2(a:Int) = { b:Int => a + b }
// Works
add2(5)(6)
// Also works
val f = add2(5)
f(10) // Yields 15
// Doesn't compile
val f = add2(5)_
The style guide incorrectly implies these are the same, when they are clearly not. The guide is trying to make a point about created curried functions, and, while the second form is not "by-the-book" currying, it's still very similar to the first form (though arguably easier to use because you don't need the _)
风格指南错误地暗示它们是相同的,而它们显然不是。该指南试图说明创建的柯里化函数,虽然第二种形式不是“按书”柯里化,但它仍然与第一种形式非常相似(尽管可以说更容易使用,因为你不需要的_)
From those that use these forms, what's the consensus on when to use one form over the other?
在使用这些形式的人中,对于何时使用一种形式而不是另一种形式的共识是什么?
回答by retronym
Multiple Parameter List Methods
多参数列表方法
For Type Inference
对于类型推断
Methods with multiple parameter sections can be used to assist local type inference, by using parameters in the first section to infer type arguments that will provide an expected type for an argument in the subsequent section. foldLeftin the standard library is the canonical example of this.
具有多个参数部分的方法可用于辅助局部类型推断,方法是使用第一部分中的参数来推断类型参数,该类型参数将为后续部分中的参数提供预期类型。foldLeft在标准库中是这方面的规范示例。
def foldLeft[B](z: B)(op: (B, A) => B): B
List("").foldLeft(0)(_ + _.length)
If this were this written as:
如果这是这样写的:
def foldLeft[B](z: B, op: (B, A) => B): B
One would have to provide more explicit types:
必须提供更明确的类型:
List("").foldLeft(0, (b: Int, a: String) => a + b.length)
List("").foldLeft[Int](0, _ + _.length)
For fluent API
流畅的API
Another use for multiple parameter section methods is to create an API that looks like a language construct. The caller can use braces instead of parentheses.
多参数节方法的另一个用途是创建一个看起来像语言结构的 API。调用者可以使用大括号代替括号。
def loop[A](n: Int)(body: => A): Unit = (0 until n) foreach (n => body)
loop(2) {
println("hello!")
}
Application of N argument lists to method with M parameter sections, where N < M, can be converted to a function explicitly with a _, or implicitly, with an expected type of FunctionN[..]. This is a safety feature, see the change notes for Scala 2.0, in the Scala References, for an background.
将 N 个参数列表应用于具有 M 个参数部分的方法,其中 N < M,可以显式转换为具有 的函数_,或隐式转换为具有预期类型的函数FunctionN[..]。这是一项安全功能,有关背景信息,请参阅 Scala 参考资料中 Scala 2.0 的更改说明。
Curried Functions
咖喱函数
Curried functions (or simply, functions that return functions) more easily be applied to N argument lists.
Curried 函数(或者简单地说,返回函数的函数)更容易应用于 N 个参数列表。
val f = (a: Int) => (b: Int) => (c: Int) => a + b + c
val g = f(1)(2)
This minor convenience is sometimes worthwhile. Note that functions can't be type parametric though, so in some cases a method is required.
这种小小的便利有时是值得的。请注意,函数不能是参数类型,因此在某些情况下需要一种方法。
Your second example is a hybrid: a one parameter section method that returns a function.
你的第二个例子是一个混合体:一个返回一个函数的单参数部分方法。
Multi Stage Computation
多阶段计算
Where else are curried functions useful? Here's a pattern that comes up all the time:
柯里化函数还有什么用处?这是一个经常出现的模式:
def v(t: Double, k: Double): Double = {
// expensive computation based only on t
val ft = f(t)
g(ft, k)
}
v(1, 1); v(1, 2);
How can we share the result f(t)? A common solution is to provide a vectorized version of v:
我们如何分享结果f(t)?一个常见的解决方案是提供一个向量化的版本v:
def v(t: Double, ks: Seq[Double]: Seq[Double] = {
val ft = f(t)
ks map {k => g(ft, k)}
}
Ugly! We've entangled unrelated concerns -- calculating g(f(t), k)and mapping over a sequence of ks.
丑陋的!我们纠缠了不相关的问题——计算g(f(t), k)和映射一系列ks.
val v = { (t: Double) =>
val ft = f(t)
(k: Double) => g(ft, k)
}
val t = 1
val ks = Seq(1, 2)
val vs = ks map (v(t))
We could also use a method that returns a function. In this case its a bit more readable:
我们还可以使用返回函数的方法。在这种情况下,它更具可读性:
def v(t:Double): Double => Double = {
val ft = f(t)
(k: Double) => g(ft, k)
}
But if we try to do the same with a method with multiple parameter sections, we get stuck:
但是,如果我们尝试对具有多个参数部分的方法执行相同操作,则会陷入困境:
def v(t: Double)(k: Double): Double = {
^
`-- Can't insert computation here!
}
回答by Landei
You can curry only functions, not methods. addis a method, so you need the _to force its conversion to a function. add2returns a function, so the _is not only unnecessary but makes no sense here.
您只能对函数进行柯里化,而不能对方法进行柯里化。add是一种方法,因此您需要_强制将其转换为函数。add2返回一个函数,所以这_不仅是不必要的,而且在这里没有意义。
Considering how different methods and functions are (e.g. from the perspective of the JVM), Scala does a pretty good job blurring the line between them and doing "The Right Thing" in most cases, but there isa difference, and sometimes you just need to know about it.
考虑到方法和函数的不同(例如从 JVM 的角度来看),Scala 在模糊它们之间的界限并在大多数情况下做“正确的事情”方面做得非常好,但还是有区别的,有时您只需要了解它。
回答by ddekany
I think it helps to grasp the differences if I add that with def add(a: Int)(b: Int): Intyou pretty much just define a method with twoparameters, only those two parameters are grouped into two parameter lists (see the consequences of that in other comments). In fact, that method is just int add(int a, int a)as far as Java (not Scala!) is concerned. When you write add(5)_, that's just a function literal, a shorter form of { b: Int => add(1)(b) }. On the other hand, with add2(a: Int) = { b: Int => a + b }you define a method that has only one parameter, and for Java it will be scala.Function add2(int a). When you write add2(1)in Scala it's just a plain method call (as opposed to a function literal).
我认为如果我补充一点,def add(a: Int)(b: Int): Int您几乎只是定义了一个带有两个参数的方法,只有这两个参数被分组到两个参数列表中,这有助于掌握差异(请参阅其他评论中的结果)。事实上,int add(int a, int a)就 Java(不是 Scala!)而言,该方法就是如此。当您编写 时add(5)_,那只是一个函数字面量,是{ b: Int => add(1)(b) }. 另一方面,当add2(a: Int) = { b: Int => a + b }您定义一个只有一个参数的方法时,对于 Java,它将是scala.Function add2(int a). 当您用add2(1)Scala编写时,它只是一个普通的方法调用(与函数字面量相反)。
Also note that addhas (potentially) less overhead than add2has if you immediately provide all parameters. Like add(5)(6)just translates to add(5, 6)on the JVM level, no Functionobject is created. On the other hand, add2(5)(6)will first create a Functionobject that encloses 5, and then call apply(6)on that.
另请注意,如果您立即提供所有参数,add则其(可能)的开销要少add2。就像在 JVM 级别add(5)(6)转换为一样add(5, 6),不会Function创建任何对象。另一方面,add2(5)(6)将首先创建一个Function包含的对象,5然后调用apply(6)它。

