Как отмечено в комментариях, пакет Memoize.jl , безусловно, является самым простым вариантом. Это требует, чтобы вы отметили метод во время определения.
Однако, безусловно, самый мощный подход заключается в использовании 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
выполняется вместо исходного определения функции
- Мы используем это, чтобы проверить, существует ли arg в словаре метаданных.
recurse(...)
говорит кассете вызвать функцию, но игнорирует верхний уровень overload
.
Теперь мы можем запустить функцию с памяткой:
Cassette.overdub(MemoizeCtx(metadata=Dict{Int,Int}()), fib, 80)
Что еще круче, так это то, что мы можем взять существующую функцию, которая вызывает fib
, и запомнить вызов fib
внутри этой функции:
function foo()
println("calling fib")
@show fib(80)
println("done.")
end
Cassette.overdub(MemoizeCtx(metadata=Dict{Int,Int}()), foo)
(Кассета все еще довольно сложна для компилятора, поэтому запуск в первый раз может занять некоторое время, но после этого будет быстрым).