是否可以强制对柯里化函数进行早期评估?
Is it possible to force early evaluation of a curried function?
考虑一个函数 returns 另一个函数:
def prepareFunction(args: List[Any]): String => Unit = {
println(s"Slow processing of $args...")
val results = args.map(a => s"processed $a")
def doSomething(s: String): Unit = {
println(s"Do something quick with $s and $results")
}
doSomething
}
这里的想法是:外部函数执行一些繁重的处理,returns内部函数使用封闭范围内定义的变量:
val doSomethingWithArgs = prepareFunction(List("arg1", "arg2", 3))
//> Slow processing of List(arg1, arg2, 3)...
doSomethingWithArgs("abc")
//> Do something quick with abc and List(processed arg1, processed arg2, processed 3)
doSomethingWithArgs("cde")
//> Do something quick with cde and List(processed arg1, processed arg2, processed 3)
请注意,外部函数只计算一次。
有了多个参数列表和 Scala 的 Currying syntax 我们可以写出类似的东西:
def prepareCurried(args: List[Any])(s: String): Unit = {
println(s"Slow processing of $args")
val results = args.map(a => s"processed $a")
def doSomething(s: String): Unit = {
println(s"Do something quick with $s and $results")
}
doSomething(s)
}
但是 "outer" 函数每次都会被计算:
val doSomethingWithOtherArgs = prepareCurried(List(4, 5, 6)) _
doSomethingWithOtherArgs("abc")
//> Slow processing of List(4, 5, 6)
//> Do something quick with abc and List(processed 4, processed 5, processed 6)
doSomethingWithOtherArgs("cde")
//> Slow processing of List(4, 5, 6)
//> Do something quick with cde and List(processed 4, processed 5, processed 6)
我的问题是,我能否以某种方式强制在下面的行中计算 prepareCurried
?
val doSomethingWithOtherArgs = prepareCurried(List(4, 5, 6)) _
换句话说,当部分应用具有多个参数列表的函数时,是否可以得到与"evaluation on definition"相同的效果?
在这种情况下,从 Scala 的反射 API 中分离出 reify
有助于了解语法是如何脱糖的:
scala> import scala.reflect.runtime.universe.{ reify, showCode }
import scala.reflect.runtime.universe.{reify, showCode}
scala> showCode(reify(prepareCurried(List(4, 5, 6)) _).tree)
res0: String =
{
val eta[=10=] = List.apply(4, 5, 6);
((s) => $read.prepareCurried(eta[=10=])(s))
}
如果没有 s
,根本无法获得 preparedCurried(eta)
部分。当您考虑如何在 JVM 上表示具有多个参数列表的方法时,这是有道理的(即,所有参数列表只是拼凑在一起)。正如上面的评论者所指出的,如果您希望能够将第二个函数的准备与其应用程序分开,您需要 return 明确地定义一个函数。
我认为如果我们将 prepareFunction
的内部结构写成函数字面量而不是 defs 会更容易看出发生了什么。
这将是您的方法最初的样子:
def prepareFunction(args: List[Any]): String => Unit = {
println(s"Slow processing of $args...")
val results = args.map(a => s"processed $a")
(s: String) => println(s"Do something quick with $s and $results")
}
这是 "equivalent" 您的柯里化方法:
def prepareCurried(args: List[Any]): Unit = (s: String) => {
println(s"Slow processing of $args")
val results = args.map(a => s"processed $a")
println(s"Do something quick with $s and $results")
}
现在很容易看出,在 prepareCurried
中,缓慢的处理是返回函数的一部分。
你对此无能为力。这就是使用多个参数列表而不是显式编写函数时失去的灵活性。您必须为每个用例选择更适合的。
考虑一个函数 returns 另一个函数:
def prepareFunction(args: List[Any]): String => Unit = {
println(s"Slow processing of $args...")
val results = args.map(a => s"processed $a")
def doSomething(s: String): Unit = {
println(s"Do something quick with $s and $results")
}
doSomething
}
这里的想法是:外部函数执行一些繁重的处理,returns内部函数使用封闭范围内定义的变量:
val doSomethingWithArgs = prepareFunction(List("arg1", "arg2", 3))
//> Slow processing of List(arg1, arg2, 3)...
doSomethingWithArgs("abc")
//> Do something quick with abc and List(processed arg1, processed arg2, processed 3)
doSomethingWithArgs("cde")
//> Do something quick with cde and List(processed arg1, processed arg2, processed 3)
请注意,外部函数只计算一次。
有了多个参数列表和 Scala 的 Currying syntax 我们可以写出类似的东西:
def prepareCurried(args: List[Any])(s: String): Unit = {
println(s"Slow processing of $args")
val results = args.map(a => s"processed $a")
def doSomething(s: String): Unit = {
println(s"Do something quick with $s and $results")
}
doSomething(s)
}
但是 "outer" 函数每次都会被计算:
val doSomethingWithOtherArgs = prepareCurried(List(4, 5, 6)) _
doSomethingWithOtherArgs("abc")
//> Slow processing of List(4, 5, 6)
//> Do something quick with abc and List(processed 4, processed 5, processed 6)
doSomethingWithOtherArgs("cde")
//> Slow processing of List(4, 5, 6)
//> Do something quick with cde and List(processed 4, processed 5, processed 6)
我的问题是,我能否以某种方式强制在下面的行中计算 prepareCurried
?
val doSomethingWithOtherArgs = prepareCurried(List(4, 5, 6)) _
换句话说,当部分应用具有多个参数列表的函数时,是否可以得到与"evaluation on definition"相同的效果?
在这种情况下,从 Scala 的反射 API 中分离出 reify
有助于了解语法是如何脱糖的:
scala> import scala.reflect.runtime.universe.{ reify, showCode }
import scala.reflect.runtime.universe.{reify, showCode}
scala> showCode(reify(prepareCurried(List(4, 5, 6)) _).tree)
res0: String =
{
val eta[=10=] = List.apply(4, 5, 6);
((s) => $read.prepareCurried(eta[=10=])(s))
}
如果没有 s
,根本无法获得 preparedCurried(eta)
部分。当您考虑如何在 JVM 上表示具有多个参数列表的方法时,这是有道理的(即,所有参数列表只是拼凑在一起)。正如上面的评论者所指出的,如果您希望能够将第二个函数的准备与其应用程序分开,您需要 return 明确地定义一个函数。
我认为如果我们将 prepareFunction
的内部结构写成函数字面量而不是 defs 会更容易看出发生了什么。
这将是您的方法最初的样子:
def prepareFunction(args: List[Any]): String => Unit = {
println(s"Slow processing of $args...")
val results = args.map(a => s"processed $a")
(s: String) => println(s"Do something quick with $s and $results")
}
这是 "equivalent" 您的柯里化方法:
def prepareCurried(args: List[Any]): Unit = (s: String) => {
println(s"Slow processing of $args")
val results = args.map(a => s"processed $a")
println(s"Do something quick with $s and $results")
}
现在很容易看出,在 prepareCurried
中,缓慢的处理是返回函数的一部分。
你对此无能为力。这就是使用多个参数列表而不是显式编写函数时失去的灵活性。您必须为每个用例选择更适合的。