按需呼叫的简单示例

时间:2019-01-18 21:45:12

标签: r haskell lazy-evaluation call-by-need

我试图理解“按需呼叫”背后的定理。我确实理解这个定义,但是我有点困惑。我想看一个简单的示例,该示例显示按需呼叫的工作方式。

在阅读了之前的一些文章之后,我发现Haskell使用了这种评估。还有其他支持此功能的编程语言吗?

我了解了Scala的按名称呼叫,并且我确实了解按名称呼叫和按需求呼叫是相似的,但不同之处在于按需求呼叫将保留评估值。但是我真的很希望看到一个真实的示例(不一定在Haskell中),该示例显示了按需呼叫。

2 个答案:

答案 0 :(得分:4)

功能

say_hello numbers = putStrLn "Hello!"

忽略其numbers参数。在call-by-value语义下,即使忽略了参数,也可能需要评估函数调用站点上的参数,这可能是由于程序其余部分所依赖的副作用所致。

在Haskell中,我们可以将say_hello称为

say_hello [1..]

其中[1..]是自然数的无限列表。在按值调用的语义下,CPU会试图建立一个无限列表而运行,根本无法访问say_hello

Haskell仅输出

$ runghc cbn.hs
Hello!

对于不太生动的例子,前十个自然数是

ghci> take 10 [1..]
[1,2,3,4,5,6,7,8,9,10]

前十个赔率是

ghci> take 10 $ filter odd [1..]
[1,3,5,7,9,11,13,15,17,19]

call-by-need语义下,每个值-甚至是如上例中概念上无限的值-都仅在要求的范围内评估,而不再进行评估。

答案 1 :(得分:1)

update:一个简单的示例,要求:

ff 0 = 1
ff 1 = 1
ff n = go (ff (n-1))
  where
  go x = x + x

在“按名称进行呼叫”下,go的每次调用都会对ff (n-1)进行两次计算,每次在其定义中x的每次出现都会被评估(因为+都严格参数,即要求两者都具有值。

在需要时,go的参数最多被评估一次。具体来说,这里x的值仅被发现一次,并在表达式x中再次用于x + x的出现。如果不需要,则x完全不会被评估,就像通过名字呼叫一样。

在按值调用方式下,go的参数在进入函数主体之前总是被精确评估一次,即使在函数主体的任何地方都没有使用它。


在Haskell的背景下,这是我的理解。

According to Wikipedia“按需调用是按名称调用的记忆形式,如果对函数自变量求值,则该值将存储起来供以后使用。”

按姓名致电:

take 10 . filter even $ [1..]

一个消费者产生的价值在产生后就消失了,所以它也可能是按名字叫。

需要致电:

import qualified Data.List.Ordered as O

h = 1 : map (2*) h <> map (3*) h <> map (5*) h
    where
    (<>) = O.union

区别在于,h列表在这里以不同的速度被多个使用者重用,因此记住产生的值至关重要。在“按名字呼叫”语言中,这里需要进行大量的计算工作,因为h的计算表达式将在每次出现时都被替换,从而导致每次计算都单独进行。在像Haskell这样需要呼叫的有能力语言中,h元素的计算结果在每个对h的引用之间共享。

另一个例子是,fix定义的大多数数据只能在按需调用的情况下才能使用。借助按值致电,我们最多可以使用 Y 组合器。

请参阅:Sharing vs. non-sharing fixed-point combinator及其链接的条目和注释(其中this its 链接,例如Can fold be used to create infinite lists?)。