我试图理解“按需呼叫”背后的定理。我确实理解这个定义,但是我有点困惑。我想看一个简单的示例,该示例显示按需呼叫的工作方式。
在阅读了之前的一些文章之后,我发现Haskell使用了这种评估。还有其他支持此功能的编程语言吗?
我了解了Scala的按名称呼叫,并且我确实了解按名称呼叫和按需求呼叫是相似的,但不同之处在于按需求呼叫将保留评估值。但是我真的很希望看到一个真实的示例(不一定在Haskell中),该示例显示了按需呼叫。
答案 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?)。