Scala 中柯里化的两种方式;每个的用例是什么?
我正在讨论多个参数列表< /em> 在我维护的 Scala 风格指南中。我开始意识到有两种柯里化的方法,我想知道什么是用例是:
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)_
样式指南错误地暗示它们是相同的,而实际上它们显然不是相同的。该指南试图阐述创建的柯里化函数的观点,并且,虽然第二种形式不是“按书本”柯里化,但它仍然与第一种形式非常相似(尽管可以说更容易使用,因为你不需要_
)
对于使用这些表单的人来说,对于何时使用一种表单而不是另一种表单有何共识?
I am having a discussion around Multiple Parameter Lists in 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:
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
多个参数列表方法
用于类型推断的
具有多个参数部分的方法可用于辅助本地类型推断,方法是使用第一部分中的参数来推断类型参数,这些参数将为后续部分中的参数提供预期类型。标准库中的
foldLeft
就是典型的例子。如果这样写:
则必须提供更明确的类型:
对于流畅的 API
多参数部分方法的另一种用途是创建一个看起来像语言构造的 API。调用者可以使用大括号代替圆括号。
将 N 个参数列表应用于具有 M 个参数部分的方法,其中 N < M,可以使用
_
显式转换为函数,也可以使用FunctionN[..]
预期类型隐式转换为函数。这是一项安全功能,有关背景信息,请参阅 Scala 参考资料中的 Scala 2.0 更改说明。柯里化函数
柯里化函数(或者简单地说,返回函数的函数)更容易应用于 N 个参数列表。
这种小小的便利有时是值得的。请注意,函数不能是参数类型,因此在某些情况下需要方法。
您的第二个示例是一个混合示例:返回函数的单参数部分方法。
多阶段计算
柯里化函数还有哪些用处?这是一个经常出现的模式:
我们如何共享结果
f(t)
?一个常见的解决方案是提供v
的矢量化版本:丑陋!我们纠缠了不相关的问题——计算 g(f(t), k) 并映射到 ks 序列。
我们还可以使用返回函数的方法。在这种情况下,它的可读性更强:
但是如果我们尝试对具有多个参数部分的方法执行相同的操作,我们就会陷入困境:
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.
foldLeft
in the standard library is the canonical example of this.If this were this written as:
One would have to provide more explicit types:
For fluent 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.
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 ofFunctionN[..]
. This is a safety feature, see the change notes for Scala 2.0, in the Scala References, for an background.Curried Functions
Curried functions (or simply, functions that return functions) more easily be applied to N argument lists.
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:
How can we share the result
f(t)
? A common solution is to provide a vectorized version ofv
:Ugly! We've entangled unrelated concerns -- calculating
g(f(t), k)
and mapping over a sequence ofks
.We could also use a method that returns a function. In this case its a bit more readable:
But if we try to do the same with a method with multiple parameter sections, we get stuck:
您只能柯里化函数,而不能柯里化方法。
add
是一个方法,因此您需要_
来强制其转换为函数。add2
返回一个函数,因此_
不仅没有必要,而且在这里毫无意义。考虑到方法和函数的不同(例如,从 JVM 的角度来看),Scala 很好地模糊了它们之间的界限,并在大多数情况下做“正确的事情”,但是有差异,有时您只需要了解它即可。
You can curry only functions, not methods.
add
is a method, so you need the_
to force its conversion to a function.add2
returns a function, so the_
is not only unnecessary but makes no sense here.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 is a difference, and sometimes you just need to know about it.
我认为如果我添加 def add(a: Int)(b: Int): Int ,你几乎只需用 two 定义一个方法,这有助于理解差异参数,只有这两个参数被分组到两个参数列表中(请参阅其他注释中的结果)。事实上,就 Java(不是 Scala!)而言,该方法只是
int add(int a, int a)
。当您编写add(5)_
时,这只是一个函数文字,是{ b: Int =>; 的缩写形式。添加(1)(b)}
。另一方面,使用 add2(a: Int) = { b: Int => a + b } 你定义了一个只有一个参数的方法,对于 Java 来说,它是 scala.Function add2(int a) 。当您在 Scala 中编写add2(1)
时,它只是一个普通的方法调用(而不是函数文字)。另请注意,如果您立即提供所有参数,则
add
的开销(可能)比add2
少。就像add(5)(6)
只是在 JVM 级别上转换为add(5, 6)
一样,没有创建Function
对象。另一方面,add2(5)(6)
将首先创建一个包含5
的Function
对象,然后调用apply (6) 对此。
I think it helps to grasp the differences if I add that with
def add(a: Int)(b: Int): Int
you pretty much just define a method with two parameters, only those two parameters are grouped into two parameter lists (see the consequences of that in other comments). In fact, that method is justint add(int a, int a)
as far as Java (not Scala!) is concerned. When you writeadd(5)_
, that's just a function literal, a shorter form of{ b: Int => add(1)(b) }
. On the other hand, withadd2(a: Int) = { b: Int => a + b }
you define a method that has only one parameter, and for Java it will bescala.Function add2(int a)
. When you writeadd2(1)
in Scala it's just a plain method call (as opposed to a function literal).Also note that
add
has (potentially) less overhead thanadd2
has if you immediately provide all parameters. Likeadd(5)(6)
just translates toadd(5, 6)
on the JVM level, noFunction
object is created. On the other hand,add2(5)(6)
will first create aFunction
object that encloses5
, and then callapply(6)
on that.