按需要拨打电话按姓名拨打电话

Ada*_* Sh 17 evaluation lazy-evaluation

我不明白Call-by-name和Call-by-need之间的差异.据我所知,Call-by-need方法恢复了返回的答案.但它如何帮助我们,结果之间是否存在根本差异?

例如,

begin integer n;
  procedure foo(e, n);
  integer e, n;
  begin
    for n := 1 step 1 until 10 do begin
      prints(`;;; the value of e is ');
      printnln(e)
    end
  end;
  foo(2 * n, n)
end
Run Code Online (Sandbox Code Playgroud)

因此,按照我的理解,在按名称呼叫时,我们将获得:

;;; the value of e is 2
;;; the value of e is 4
;;; the value of e is 8
Run Code Online (Sandbox Code Playgroud)

等等.这是因为我们通过2*ne,并e与新的评估i每次.在需要召唤时会发生什么?

Dan*_*yes 10

你的困惑似乎源于你在一个命令式的背景下思考的事实.讨论的call-by-需要呼叫按值大多出现有关声明和函数式语言,和演算.

您可以在本文中看到评估策略,即按名称调用按需调用都被视为惰性评估策略.延迟评估意味着当表达式作为参数传递给函数时,在进入函数体之前不会对其进行求值,而只是在函数内第一次访问/读取时才进行求值.如果从不在内部使用此类表达式的结果,则永远不会对其进行求值.

例如,? :运算符在Java中是惰性的,如下面的代码所示:

String test(Object obj)
{
    return 1 == 2 ? obj.toString() : "Hello World";
}

test(null); // this won't throw a NullPointerException
Run Code Online (Sandbox Code Playgroud)

按需调用是具有子集的大多数函数语言的基本特征.在纯函数式语言中,每个函数都必须是引用透明的,即它们不能有副作用.这样的纯函数具有这样的特性:对于某些给定的输入,它们总是返回相同的输出,无论调用多少次,并且它们在"世界状态"中永远不会改变任何东西.它们的行为就像写在纸上的数学函数一样.

正如你已经意识到,调用-需要调用非纯函数时的策略是行不通的,因为你是最有可能感兴趣的副作用,由于连续的调用.另一方面,当在纯函数语言中使用时,它成为性能的基本特征(参见下面的第二个示例).另外,请参阅这些wiki页面,了解Graph ReductionMemoization的概念.

现实世界的例子

第一.使用图缩减的常用系统的一个示例是Apache Ant.Ant不会两次评估目标.此设计可以方便地绘制声明性构建计划.

第二.如果你想看到一个很好的memoization演示,输入这个Haskell代码到GHC解释器,看看会发生什么:

Prelude> let fibs = 0:1:(zipWith (+) fibs (tail fibs))
-- This defines the Fibonacci sequence.
Prelude> fibs !! 200000
-- Prints the 200,000th Fibonacci number,
-- takes several seconds to calculate.
Prelude> fibs !! 200000
-- Prints the same number as before,
-- but this time it returns immediately.
Run Code Online (Sandbox Code Playgroud)

注意.您可能还听说过按值调用的评估策略.与此相反的call-by-名称调用-需要,调用-值是一个严格的评估策略.它像调用式的名字在这个意义上,多次调用导致多次评估.对于习惯于C#或Java等命令式语言的程序员来说,这是最常见的范例.


Mar*_*cin 8

Call-by-name是一个函数调用规则,当调用一个接收函数foo时,而不是foo被赋值的参数,foo接收(在幕后)一个适当的对象,允许它评估它的参数需要; 或者等价地,通过宏观替代进行评估.如果一个参数需要多次,则将对其进行多次评估.请参阅:http://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_name

按需调用是一样的,除了传递的对象是一个承诺,并且将被评估不超过一次; 在对参数的后续引用中,使用记忆值.请参阅:http://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_need


Rai*_*ruz 5

首先,无论是call-by-need还是call-by-name都代表了惰性求值的实现方式,所以有必要知道什么是惰性求值......

例如,您可以在 Haskell 中看到按需要调用,在 Scala 中看到按名称调用。

按需要调用可能是实现惰性的预期方式。当您第一次“真正”需要该值时,您会计算它并缓存计算值以供将来访问。

当您使用按名称调用时,您不会执行这种缓存,而是每次在函数体中使用函数参数时都会对其求值。可能会认为它没有意义,但是将一些流控制结构实现为语言的函数是有用的,比如说一段时间。

def myWhile (cond : => Boolean, body : => Unit) {
  if (cond) { body ; myWhile (cond, body) }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以调用函数 myWhile,

var x = 3

myWhile (x != 0, {
  print (x)
  x = x - 1   
})
Run Code Online (Sandbox Code Playgroud)

如果我们在上面的示例中有按名称调用,则表达式“cond”不会被缓存,并且每次需要时都会对其进行求值。


Ada*_* Sh -2

在按需调用中,我们进入循环,并且仅对值求值一次。因此,在上面的代码中,我们将(2*n)在循环内部进行复制(宏样式),并且我们将仅对表达式求值一次(不像按名称调用)。因此,在第一次迭代中我们将得到e=2. 这将是下一次迭代中的值e,输出将为:

;;; the value of e is 2
;;; the value of e is 2
;;; the value of e is 2
Run Code Online (Sandbox Code Playgroud)