Scala 中柯里化的两种方式;每个的用例是什么?

发布于 2024-10-15 21:34:59 字数 735 浏览 1 评论 0原文

我正在讨论多个参数列表< /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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

肤浅与狂妄 2024-10-22 21:34:59

多个参数列表方法

用于类型推断的

具有多个参数部分的方法可用于辅助本地类型推断,方法是使用第一部分中的参数来推断类型参数,这些参数将为后续部分中的参数提供预期类型。标准库中的 foldLeft 就是典型的例子。

def foldLeft[B](z: B)(op: (B, A) => B): B

List("").foldLeft(0)(_ + _.length)

如果这样写:

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)

对于流畅的 API

多参数部分方法的另一种用途是创建一个看起来像语言构造的 API。调用者可以使用大括号代替圆括号。

def loop[A](n: Int)(body: => A): Unit = (0 until n) foreach (n => body)

loop(2) {
   println("hello!")
}

将 N 个参数列表应用于具有 M 个参数部分的方法,其中 N < M,可以使用 _ 显式转换为函数,也可以使用 FunctionN[..] 预期类型隐式转换为函数。这是一项安全功能,有关背景信息,请参阅 Scala 参考资料中的 Scala 2.0 更改说明。

柯里化函数

柯里化函数(或者简单地说,返回函数的函数)更容易应用于 N 个参数列表。

val f = (a: Int) => (b: Int) => (c: Int) => a + b + c
val g = f(1)(2)

这种小小的便利有时是值得的。请注意,函数不能是参数类型,因此在某些情况下需要方法。

您的第二个示例是一个混合示例:返回函数的单参数部分方法。

多阶段计算

柯里化函数还有哪些用处?这是一个经常出现的模式:

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);

我们如何共享结果f(t)?一个常见的解决方案是提供 v 的矢量化版本:

def v(t: Double, ks: Seq[Double]: Seq[Double] = {
   val ft = f(t)
   ks map {k => g(ft, k)}
}

丑陋!我们纠缠了不相关的问题——计算 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))

我们还可以使用返回函数的方法。在这种情况下,它的可读性更强:

def v(t:Double): Double => Double = {
   val ft = f(t)
   (k: Double) => g(ft, k)       
}

但是如果我们尝试对具有多个参数部分的方法执行相同的操作,我们就会陷入困境:

def v(t: Double)(k: Double): Double = {
                ^
                `-- Can't insert computation here!
}

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.

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

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.

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.

Curried Functions

Curried functions (or simply, functions that return functions) more easily be applied to N argument lists.

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:

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.

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!
}
甜柠檬 2024-10-22 21:34:59

您只能柯里化函数,而不能柯里化方法。 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.

爱你不解释 2024-10-22 21:34:59

我认为如果我添加 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) 将首先创建一个包含 5Function 对象,然后调用 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 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).

Also note that add has (potentially) less overhead than add2 has if you immediately provide all parameters. Like add(5)(6) just translates to add(5, 6) on the JVM level, no Function object is created. On the other hand, add2(5)(6) will first create a Function object that encloses 5, and then call apply(6) on that.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文