为什么这个版本的“修复”在Haskell中更有效率?

在Haskell中,这是一个固定点的简单(天真)定义

fix :: (a -> a) -> a
fix f = f (fix f)

但是,这里是Haskell实际实现的方式(效率更高)

fix f = let x = f x in x

我的问题是为什么第二个比第一个更有效率?

慢修复在递归的每个步骤上调用f,而快速调用f恰好是一次。它可以跟踪进行可视化:

import Debug.Trace

fix  f = f (fix f)
fix' f = let x = f x in x

facf :: (Int -> Int) -> Int -> Int
facf f 0 = 1
facf f n = n * f (n - 1)

tracedFacf x = trace "called" facf x

fac  = fix tracedFacf
fac' = fix' tracedFacf

现在尝试一下运行:

> fac 3
called
called
called
called
6
> fac' 3
called
6

更详细地说,x中的x = f x导致一个懒惰的thunk被分配给x,并且一个指向这个thunk的指针被传递给f。在第一次评估fix’f时,thunk被评估,并且对它的所有引用(这里具体来说:我们传递给f的那个)被重定向到结果值。它只是发生在x被赋予一个值,它还包含对x的引用。

我承认这可以是相当的思想弯曲。这是一个应该习惯于懒惰工作的东西。

http://stackoverflow.com/questions/37366222/why-is-this-version-of-fix-more-efficient-in-haskell

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:为什么这个版本的“修复”在Haskell中更有效率?