考虑到在 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 的目的是啥?的主要内容,如果未能解决你的问题,请参考以下文章