正如评论中指出的那样,记忆化.jl https://github.com/simonster/Memoize.jl包当然是最简单的选择。这需要您在定义时标记该方法。
然而,迄今为止最有效的方法是使用卡带.jl https://github.com/jrevels/Cassette.jl,它允许您将记忆添加到预先存在的函数中,例如
fib(x) = x < 3 ? 1 : fib(x-2) + fib(x-1)
using Cassette
Cassette.@context MemoizeCtx
function Cassette.overdub(ctx::MemoizeCtx, ::typeof(fib), x)
get(ctx.metadata, x) do
result = recurse(ctx, fib, x)
ctx.metadata[x] = result
return result
end
end
对正在发生的事情进行一些描述:
-
MemoizeCtx
是我们定义的盒式磁带“上下文”
-
overdub https://jrevels.github.io/Cassette.jl/latest/api.html#Cassette.overdub is run instead of the original function definition
- 我们用它来检查 arg 是否存在于元数据字典中。
-
recurse(...)
告诉 Cassette 调用该函数,但忽略顶层overload
.
现在我们可以通过记忆来运行该函数:
Cassette.overdub(MemoizeCtx(metadata=Dict{Int,Int}()), fib, 80)
现在更酷的是我们可以使用一个现有的函数来调用fib
,并记住对的调用fib
inside该函数:
function foo()
println("calling fib")
@show fib(80)
println("done.")
end
Cassette.overdub(MemoizeCtx(metadata=Dict{Int,Int}()), foo)
(卡带对编译器来说仍然相当困难,因此第一次运行可能需要一段时间,但之后会很快)。