我了解按名称调用和按值调用的基本概念,并且我还研究了一些示例。但是,我不太清楚何时使用按名称调用。与其他呼叫类型相比,按名称呼叫将具有显着优势或性能提升的真实场景是什么?在设计方法时选择调用类型的正确思路应该是什么?
5 回答
有很多地方可以通过名称来获得性能甚至是正确性。
简单的性能示例:日志记录。想象一个这样的界面:
trait Logger {
def info(msg: => String)
def warn(msg: => String)
def error(msg: => String)
}
然后像这样使用:
logger.info("Time spent on X: " + computeTimeSpent)
如果该info
方法不执行任何操作(例如,日志级别配置为高于该级别),则computeTimeSpent
永远不会被调用,从而节省时间。记录器经常发生这种情况,人们经常看到字符串操作相对于正在记录的任务而言可能是昂贵的。
正确性示例:逻辑运算符。
你可能见过这样的代码:
if (ref != null && ref.isSomething)
假设您声明&&
了这样的方法:
trait Boolean {
def &&(other: Boolean): Boolean
}
然后,无论何时ref
是null
,你都会得到一个错误,因为在传递给之前isSomething
会在引用上调用。因此,实际的声明是:null
&&
trait Boolean {
def &&(other: => Boolean): Boolean =
if (this) other else this
}
所以人们实际上可能想知道何时使用按值调用。事实上,在 Haskell 编程语言中,一切的工作方式都类似于按名称调用的工作方式(类似,但不一样)。
不使用 call-by-name 有很好的理由:它更慢,它创建更多的类(意味着程序需要更长的加载时间),它消耗更多的内存,而且它的不同之处足以让许多人难以理解它。
按名称调用意味着在访问值时对其进行评估,而按值调用则首先对值进行评估,然后将其传递给方法。
要查看差异,请考虑这个示例(完全非函数式编程,只有副作用;))。假设您想创建一个函数来测量某些操作需要多少时间。您可以通过名称调用来做到这一点:
def measure(action: => Unit) = {
println("Starting to measure time")
val startTime = System.nanoTime
action
val endTime = System.nanoTime
println("Operation took "+(endTime-startTime)+" ns")
}
measure {
println("Will now sleep a little")
Thread.sleep(1000)
}
你会得到结果(YMMV):
Starting to measure time
Will now sleep a little
Operation took 1000167919 ns
但是,如果您仅更改 to 的签名measure
以measure(action: Unit)
使其使用按值传递,则结果将是:
Will now sleep a little
Starting to measure time
Operation took 1760 ns
如您所见,甚至在开始action
之前就评估了,measure
并且由于在调用方法之前已经运行了操作,因此经过的时间也接近于 0。
在这里,按名称传递允许实现方法的预期行为。在某些情况下,它不会影响正确性,但会影响性能,例如在日志框架中,如果不使用结果,可能根本不需要评估复杂的表达式。
可能解释的简单方法是
值调用函数在调用函数之前计算传入表达式的值,因此每次都访问相同的值。但是,按名称调用函数每次访问时都会重新计算传入表达式的值。
我一直认为这个术语是不必要的混乱。一个函数可以有多个参数,它们的名称调用和值调用状态各不相同。所以并不是一个函数是按名称调用或按值调用,而是它的每个参数可能是按名称传递或按值传递。此外,“按名称呼叫”与名称无关。=> Int 是与 Int 不同的类型;它是“将生成 Int 的无参数函数”与仅 Int 相比。一旦您拥有一流的功能,您就不需要发明名称调用术语来描述这一点。
当在函数中多次使用按名称调用参数时,该参数会被多次计算。
由于传入的参数应该是每个函数式编程的纯函数调用,因此被调用函数中的每次评估将始终生成相同的结果。因此,按名称调用会比传统的按值调用更浪费。
对于按名称调用的日志记录用例来说很好: https ://www.tutorialspoint.com/scala/functions_call_by_name.htm#:~:text=For%20this%20circumstance%2C%20Scala%20offers,and%20the% 20value%20is%20calculated。