在Haskell中,这是固定点的简单(幼稚)定义
fix :: (a -> a) -> a
fix f = f (fix f)
但是,以下是Haskell实际实现它的方式(更有效)
fix f = let x = f x in x
我的问题是,为什么第二个比第一个更有效?
发布于 2016-05-22 02:01:47
慢的fix
在递归的每一步都调用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
更详细地说,let x = f x in x
会导致为x
分配一个懒惰的thunk,并将指向此thunk的指针传递给f
。在第一次计算fix' f
时,thunk会被计算,所有对它的引用(这里特别是:我们传递给f
的引用)都会被重定向到结果值。恰好x
被赋予了一个也包含对x
的引用的值。
我承认这可能是相当令人费解的。这是一个人在与懒惰一起工作时应该习惯的。
https://stackoverflow.com/questions/37366222
复制相似问题