使用辅助符号记忆
可以修改问题中展示的记忆技术,以便在需要清除缓存时不需要重新建立g
和的定义。h
这个想法是将记忆值存储在辅助符号上,而不是直接存储在g
and上h
:
g[0,k_] = 0;
g[t_,0] = e;
g[t_,k_] := memo[g, t, k] /. _memo :> (memo[g, t, k] = g[t-1,k]*a+h[t-1,k-1]*b)
h[0,k_] = 0;
h[t_,0] = f;
h[t_,k_] := memo[h, t, k] /. _memo :> (memo[h, t, k] = h[t-1,k]*c+g[t-1,k-1]*d)
除了引入了一个新符号 , 之外,这些定义与g
和的原始记忆版本基本相同。有了这些定义,就可以简单地清除缓存——无需重新定义和重新定义。更好的是,缓存可以通过放置in来本地化,因此:h
memo
Clear@memo
g
h
memo
Block
Block[{memo, a = 7, b = 9, c = 13, d = 0.002, e = 2, f = 1}
, Table[g[t, k], {t, 0, 100}, {k, 0, 100}]
]
当块退出时缓存被丢弃。
使用建议进行记忆
原始和修改后的记忆技术需要在功能g
和h
. 有时,事后引入 memoization 很方便。做到这一点的一种方法是使用建议技术——一种类似于 OO 编程中的子类化的函数式编程。函数建议的特定实现定期出现在 StackOverflow 的页面中。然而,该技术也是侵入性的。让我们考虑一种在不改变全局定义的情况下添加建议g
的替代技术。h
诀窍是临时重新定义g
和h
在Block
. 重新定义将首先检查缓存中的结果,如果失败,则从块外部调用原始定义。让我们回到最初的定义g
和h
幸福地不知道记忆:
g[0,k_]:=0
g[t_,0]:=e
g[t_,k_]:=g[t-1,k]*a+h[t-1,k-1]*b
h[0,k_]:=0
h[t_,0]:=f
h[t_,k_]:=h[t-1,k]*c+g[t-1,k-1]*d
该技术的基本架构如下所示:
Module[{gg, hh}
, copyDownValues[g, gg]
; copyDownValues[h, hh]
; Block[{g, h}
, m:g[a___] := m = gg[a]
; m:h[a___] := m = hh[a]
; (* ... do something with g and h ... *)
]
]
引入临时符号gg
和hh
以保存 和 的原始g
定义h
。然后在本地重新绑定到新的缓存定义g
,h
这些定义在必要时委托给原始定义。这是 的定义copyDownValues
:
ClearAll@copyDownValues
copyDownValues[from_Symbol, to_Symbol] :=
DownValues[to] =
Replace[
DownValues[from]
, (Verbatim[HoldPattern][from[a___]] :> d_) :> (HoldPattern[to[a]] :> d)
, {1}
]
为了保持这篇文章的简短(呃),这个“复制”功能只关注向下值。一般建议工具还需要考虑上值、子值、符号属性等。
这种通用模式很容易实现自动化,即使很乏味。以下宏函数memoize
执行此操作,几乎没有评论:
ClearAll@memoize
SetAttributes[memoize, HoldRest]
memoize[symbols:{_Symbol..}, body_] :=
Module[{pairs, copy, define, cdv, sd, s, m, a}
, pairs = Rule[#, Unique[#, Temporary]]& /@ symbols
; copy = pairs /. (f_ -> t_) :> cdv[f, t]
; define = pairs /. (f_ -> t_) :> (m: f[a___]) ~sd~ (m ~s~ t[a])
; With[{ temps = pairs[[All, 2]]
, setup1 = Sequence @@ copy
, setup2 = Sequence @@ define }
, Hold[Module[temps, setup1; Block[symbols, setup2; body]]] /.
{ cdv -> copyDownValues, s -> Set, sd -> SetDelayed }
] // ReleaseHold
]
g
经过一番折腾,我们现在可以在外部对and的非缓存版本进行记忆化h
:
memoize[{g, h}
, Block[{a = 7, b = 9, c = 13, d = .002, e = 2, f = 1}
, Table[g[t, k], {t, 0, 100}, {k, 0, 100}]
]
]
把它们放在一起,我们现在可以创建一个响应Manipulate
块:
Manipulate[
memoize[{g, h}
, Table[g[t, k], {t, 0, tMax}, {k, 0, kMax}] //
ListPlot3D[#, InterpolationOrder -> 0, PlotRange -> All, Mesh -> None] &
]
, {{tMax, 10}, 5, 25}
, {{kMax, 10}, 5, 25}
, {{a, 7}, 0, 20}
, {{b, 9}, 0, 20}
, {{c, 13}, 0, 20}
, {{d, 0.002}, 0, 20}
, {{e, 2}, 0, 20}
, {{f, 1}, 0, 20}
, LocalizeVariables -> False
, TrackedSymbols -> All
]
LocalizeVariables
和TrackedSymbols
选项是通过. _ g
_h
a
f