Here是以下memoization函数的完整可运行代码:
memo f = g
where
fz = f Z
fs = memo (f . S)
g Z = fz
g (S n) = fs n
-- It is a BAD BUG to inline 'fs' inside g
-- and that happened in 6.4.1, resulting in exponential behaviour
-- memo f = g (f Z) (memo (f . S))
-- = g (f Z) (g (f (S Z)) (memo (f . S . S)))
-- = g (f Z) (g (f (S Z)) (g (f (S (S Z))) (memo (f . S . S . S))))
fib' :: Nat -> Integer
fib' = memo fib
where
fib Z = 0
fib (S Z) = 1
fib (S (S n)) = fib' (S n) + fib' n
我试图通过手动扩展这些术语来解决这个问题,但是这种扩展看起来就像是缓慢的,未经修饰的功能。它是如何工作的?注释掉的代码是如何派生出来的?
答案 0 :(得分:7)
解释起来非常棘手。我将从一个更简单的例子开始。
必须记住
之间的区别\x -> let fz = f 0 in if x==0 then fz else f x
let fz = f 0 in \x -> if x==0 then fz else f x
两者都计算相同的功能。但是,当使用参数f 0
调用时,前者将始终(重新)计算0
。相反,后者仅在第一次使用参数f 0
调用时计算0
- 当发生这种情况时,fz
被评估,并且结果永久存储在那里,以便它可以下次需要fz
时再次重复使用。
这与
没什么不同f 0 + f 0
let fz = f 0 in fz + fz
后者只会调用f 0
一次,因为第二次fz
已经过评估。
因此,我们可以实现f
的轻量级记忆,仅存储f 0
,如下所示:
g = let fz = f 0 in \x -> if x==0 then fz else f x
等效地:
g = \x -> if x==0 then fz else f x
where
fz = f 0
请注意,我们无法在\x ->
的左侧显示=
,否则我们会失去记忆!
等效地:
g = g'
where
fz = f 0
g' = \x -> if x==0 then fz else f x
现在我们可以将\x ->
放在左边,没有任何问题。
等效地:
g = g'
where
fz = f 0
g' x = if x==0 then fz else f x
等效地:
g = g'
where
fz = f 0
g' 0 = fz
g' x = f x
现在,这只会记住f 0
而不是每个f n
。实际上,计算g 4
两次将导致f 4
计算两次。
为避免这种情况,我们可以开始g
处理任何函数f
而不是固定函数:
g f = g' -- f is now a parameter
where
fz = f 0
g' 0 = fz
g' x = f x
现在,我们利用它:
-- for any f, x
g f x = f x
-- hence, in particular
g (f . succ) (pred x) = (f . succ) (pred x) = f (succ (pred x)) = f x
因此,g (f . succ) (pred x)
是编写f x
的复杂方式。像往常一样,g
将函数记忆为零。但这是(f . succ) 0 = f 1
。通过这种方式,我们获得了1
的备忘录,而不是!
因此,我们可以递归
g f = g' -- f is now a parameter
where
fz = f 0
g' 0 = fz
g' x = g (f . succ) (pred x)
如果使用0
进行调用,则会使用fz
来存储f 0
,并将其记忆。
如果使用1
进行调用,则会调用g (f . succ)
,这将为fz
案例分配另一个1
。
这看起来不错,但是fz
不会持续很长时间,因为每次调用g' x
时它都会被重新分配,从而否定了memoization。
要解决此问题,我们会使用另一个变量,因此g (f . succ)
最多只计算一次。
g f = g' -- f is now a parameter
where
fz = f 0
fs = g (f . succ)
g' 0 = fz
g' x = fs (pred x)
此处,fs
最多只评估一次,并会为fz
案例分配另一个1
。此fz
现在不会消失。
递归地说,人们可以确信现在所有的值f n
都被记忆了。