考虑到在 Scala 中返回函数的其他替代方法,Currying 的目的是啥?

Posted

技术标签:

【中文标题】考虑到在 Scala 中返回函数的其他替代方法,Currying 的目的是啥?【英文标题】:What's the purpose of Currying given other alternatives to return a function in Scala?考虑到在 Scala 中返回函数的其他替代方法,Currying 的目的是什么? 【发布时间】:2021-11-28 00:41:58 【问题描述】:

我目前正在学习 Scala 课程,最近我被介绍了返回函数的不同技术。

例如,给定这个函数和方法:

val simpleAddFunction = (x: Int, y: Int) => x + y
def simpleAddMethod(x: Int, y: Int) = x + y

这样做我可以返回另一个函数:

val add7_v1 = (x: Int) => simpleAddFunction(x, 7)
val add7_v2 = simpleAddFunction(_: Int, 7)
val add7_v3 = (x: Int) => simpleAddMethod(x, 7)
val add7_v4 = simpleAddMethod(_: Int, 7)

所有的值 add7_x 完成同样的事情,那么,Currying 的目的是什么? 如果上述所有功能都具有相似的功能,为什么我必须写def simpleCurryMethod(x: Int)(y: Int) = x + y

就是这样!我是函数式编程的新手,除了通过减少重复使用参数来节省时间之外,我不知道很多 Currying 的用例。因此,如果有人能解释一下柯里化相对于前面的例子或一般柯里化的优势,我将不胜感激。

就是这样,祝你有美好的一天!

【问题讨论】:

我不了解 Scala,但总的来说,currying 的主要原因是为了简化函数接口。如果函数总是声明一个参数,你就不必再担心它们的数量了。这些函数本质上是可组合的并且部分适用。现在看来 Scala 也支持部分应用的命令式方式。这并不奇怪,因为实现目标的方法通常不止一种。 【参考方案1】:

Scala 2 中,使用 METHODS 方法只有四个实用的原因(据我所知,如果有人有另一个有效的用例,请告诉我) .

    (可能也是使用它的主要原因) 来驱动类型推断。

例如,当您想要接受一个函数或其他类型的泛型值时,其泛型类型应该从一些普通数据中推断出来。例如:

def applyTwice[A](a: A)(f: A => A): A = f(f(a))

applyTwice(10)(_ + 1) // Here the compiler is able to infer that f is Int => Int

在上面的示例中,如果我没有对函数进行柯里化,那么我需要执行以下操作:applyTwice(10, (x: Int) => x + 1) 来调用该函数。 这是多余的,看起来更糟(恕我直言)

注意:Scala 3 中,类型推断得到了改进,因此这个原因在那里不再有效。

    (并且可能是现在 Scala 3 中的主要原因) 调用者的用户体验。

例如,如果您希望参数是函数或块,通常最好将其作为单独的参数放在其自己的 (和最后一个) 参数列表中,这样使用起来看起来不错。例如:

def iterN(n: Int)(body: => Unit): Unit =
  if (n > 0) 
    body
    iterN(n - 1)(body)
  

iterN(3) 
  println("Hello")
  // more code
  println("World")

同样,如果我不使用以前的方法,那么用法会是这样的:

iterN(3, 
  println("Hello")
  // more code
  println("World")
)

这看起来不太好:)

    (根据我的经验很奇怪但有效)当你知道大多数用户会部分调用它来返回一个函数时。

因为val baz = foo(bar) _ 看起来比val baz = foo(bar, _) 好,而对于第一个,您有时不会使用下划线,例如:data.map(foo(bar))

注意:免责声明,我个人认为如果是这种情况,最好立即返回一个函数而不是currying。


编辑

感谢@jwvh 指出第四个用例。

    (在使用依赖路径的类型时很有用) 当您需要参考之前的参数时。例如:
trait Foo 
  type I

  def bar(i: I): Baz


def run(foo: Foo)(i: foo.I): Baz =
  foo.bar(i)

【讨论】:

4.参数引用之前的参数:def f(x:Int)(y:Int = x):Unit = () 非常感谢!我还发现了另一种用法:Scala 中的函数最多只能接受 22 个参数(感谢 Function22 特征)。如果由于某种原因您需要一个需要更多功能的函数,您可以将这些参数归结为您的需要。无论如何,非常感谢您的解释,祝您愉快! @jwvh 哦,那很好,我明天将编辑答案。顺便说一句,你知道这是否是 Scala 3 上的 "fixed" 吗? (我明天可以检查). @LuisMiguelMejíaSuárez; Scala-3 在某些情况下更好。评论this Scastie。 有点切题,但柯里化也意味着在对 Scala 进行任何形式的推理时,您可以安全地假设函数最多接受一个参数。

以上是关于考虑到在 Scala 中返回函数的其他替代方法,Currying 的目的是啥?的主要内容,如果未能解决你的问题,请参考以下文章

考虑使用静态工厂方法替代构造函数

Scala的方法和函数

Scala 基础知识

c语言gets()函数与它的替代者fgets()函数

Scala - 多个函数,返回多个函数

如果在Scala中返回错误