Scala 是否可以强制对当前函数进行早期评估?

Scala 是否可以强制对当前函数进行早期评估?,scala,currying,Scala,Currying,考虑一个返回另一个函数的函数: 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 a

考虑一个返回另一个函数的函数:

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
}
这里的想法是:外部函数执行一些繁重的处理,并返回一个使用封闭范围中定义的变量的内部函数:

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,我们可以编写类似的内容:

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)
}  
但每次都会对外部函数求值:

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

换言之,是否可能获得与部分应用具有多个参数列表的函数时相同的效果

在这种情况下,从Scala的反射API中进行具体化有助于了解语法是如何被删除的:

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$0$1 = List.apply(4, 5, 6);
  ((s) => $read.prepareCurried(eta$0$1)(s))
}

如果没有s,根本无法获得准备好的Curriedeta零件。当您考虑如何在JVM上表示具有多个参数列表的方法时,这是有意义的,即所有的参数列表只是被粉碎在一起。正如上面的评论员所指出的,如果您希望能够将第二个函数的准备工作与其应用程序分开,则需要显式返回一个函数。

我认为,如果我们将prepareFunction的内部编写为函数文本而不是DEF,将更容易看到发生了什么

这就是您的方法最初的样子:

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")
}
这相当于你的咖喱方法:

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中,缓慢的处理是返回函数的一部分


你对此无能为力。这就是在显式编写函数时使用多个参数列表所失去的灵活性。您必须为每个用例选择更适合的选项。

根据定义,这正是curried函数的行为方式,我想不出任何理由来解释您为什么要更改它。在模式1中,您已经有了另一个选择。嘿@Sarvesh,没有特别的原因,我只是想知道curry和返回函数的函数之间的对称性。很有趣。出于好奇,我尝试了showCodereifyList1,2,3.foldLeft0 u0.tree,模拟结果op=>eta$0$1.foldLeft0op。有道理,所以,我们真正得到的是围绕原始函数的包装函数,对吗?prepareCurried实际上是args=>s=>prepareCurriedargss。@AnthonyAccioly是的,但是在Function1 sense prepareCurried中并没有真正的原始函数。prepareCurried只是对匿名函数定义进行修改的语法。