【问题标题】:localize memoization inside a function in Julia在 Julia 的函数中本地化记忆
【发布时间】:2016-02-19 20:33:06
【问题描述】:

有没有一种方法可以在函数中本地化记忆(通过 Memoize.jl)?或者至少删除记忆创建的字典?

澄清:假设我定义了一个函数 f(x, y)。我想为每个新的 y 值创建一个新表。也就是说,给定 y = y0,f( . , y0) 对 x、x-1 等进行自我迭代,但给定一个新的 y = y1,我不需要为 y0 存储旧表,这样内存就可以被释放。我该怎么做?

解决方案:

cachedfib() = begin
    global dict = Dict()
    global dict2 = Dict()
    function _fib(n::Int, a::Int)
        if !haskey(dict2, a)
            dict2[a] = true
            dict = Dict()
        end
        if haskey(dict, (n, a))
            return dict[(n, a)]
        elseif n < 2
            dict[(0, a)] = 0
            dict[(1, a)] = 1
            return dict[(n, a)]
        else
            dict[(n, a)] = a*(_fib(n - 1, a) + _fib(n - 2, a))
            return dict[(n, a)]
        end
    end
end
fib = cachedfib()

fib(10, 1)
fib(10, 2)

现在调用dictdict2 并检查字典是否在每次第二个参数更改时刷新。当要存储的参数是整数并且使用Array 而不是Dict 时,您可以获得更好的性能

【问题讨论】:

    标签: julia memoization


    【解决方案1】:

    要使用记忆技术,您可以使用 let 或闭包。看看我对factorial 的快速实现(带闭包)。

    with_cached_factorial() = begin
        local _cache = [1] #cache factorial(0)=1    
        function _factorial(n)          
            if n < length(_cache) 
                println("pull out from the cache factorial($n)=$(_cache[n+1])")
                _cache[n+1]
            else
                fres =  n * _factorial(n-1)
                push!(_cache, fres)
                println("put factorial($n)=$fres into the cache of the size=$(sizeof(_cache))") #a
                fres            
            end
        end 
    end
    

    现在,只需使用它:

    julia> myf = with_cached_factorial()
    _factorial (generic function with 1 method)
    
    julia> myf(3)
    pull out from the cache factorial(0)=1
    put factorial(1)=1 into the cache of the size=16
    put factorial(2)=2 into the cache of the size=24
    put factorial(3)=6 into the cache of the size=32
    6
    
    julia> myf(5)
    pull out from the cache factorial(3)=6
    put factorial(4)=24 into the cache of the size=40
    put factorial(5)=120 into the cache of the size=48
    120
    
    julia> myf(10)
    pull out from the cache factorial(5)=120
    put factorial(6)=720 into the cache of the size=56
    put factorial(7)=5040 into the cache of the size=64
    put factorial(8)=40320 into the cache of the size=72
    put factorial(9)=362880 into the cache of the size=80
    put factorial(10)=3628800 into the cache of the size=88
    3628800
    

    【讨论】:

    • 感谢@maciek-leks,我尝试使用@tholy 示例中的let 块,但运行后它似乎没有释放内存,请检查我上面的编辑。
    • @amrods,这是记忆技术的经验法则吗?我编辑了我的答案,添加了缓存的大小。因此,将新值添加到缓存中会随着 new_cache_size-old_cache_size 的值增加 _factorial 函数的大小。恕我直言 whos() 将这些绑定变量大小添加到作用域函数的范围内。
    • 我看到了@maciek。但是,假设我定义了一个函数f(x, y)。我想为每个新的 y 值创建一个新表。也就是说,给定 y = y0,f(., y0) 对 x、x-1 等进行自我迭代,但给定一个新的 y = y1,我不需要为 y0 存储旧表,这样内存就可以被释放。我该怎么做?
    • 我希望我能理解。因此,let 块非常危险,因为它在每次调用代码时都会创建一个新范围。这不是问题,但它是故意将变量注入函数的,即在 for 循环中。所以,我建议使用闭包并测试是否有新的 y 值出现。如果是这样,请通过创建缓存来重新创建缓存。即在我的例子中,让't'扮演你的'y'的角色。我会这样做:“function _factorial(n, t=false) if t _cache = [1] end if n
    • @amrods,调用 with_cached_factorial 它返回一个带有一个参数的函数 _factorial。 with_cached_factorial 里面的“function _factorial(n)”这行其实和“return function _factorial(n)”是一样的。就像在函数式语言中一样,函数是“第一类对象”。这意味着您可以像其他东西一样退货。所以,这就是为什么在调用 with_cached_factorial 之后你可以访问 _factorial 函数。
    【解决方案2】:
    let Aold = nothing
    global foo
    function foo(A::AbstractArray)
        if A == Aold
            println("Same as last array")
        else
            Aold = A
        end
        nothing
    end
    end
    

    结果:

    julia> A = rand(2,2)
    2x2 Array{Float64,2}:
     0.272936  0.153311
     0.299549  0.703668
    
    julia> B = rand(2,2)
    2x2 Array{Float64,2}:
     0.6762    0.377428
     0.493344  0.240194
    
    julia> foo(A)
    
    julia> foo(A)
    Same as last array
    
    julia> foo(B)
    
    julia> foo(A)
    
    julia> foo(A)
    Same as last array
    
    julia> Aold
    ERROR: UndefVarError: Aold not defined
    

    【讨论】:

    • 谢谢@tholy,所以我需要做的就是将函数定义包装在let 中?我以前没见过这种结构。
    • 我可以以某种方式使用 Memoize.jl 吗?从@memoize 创建的对象留在内存中的意义上来说,它似乎不起作用......
    • 这与 Memoize.jl 无关。
    • 如果您想使用 Memoize.jl,最好​​在该存储库中打开一个问题。
    猜你喜欢
    • 1970-01-01
    • 2018-09-18
    • 2017-07-29
    • 2021-03-30
    • 2022-12-17
    • 2017-06-27
    • 1970-01-01
    • 1970-01-01
    • 2014-06-02
    相关资源
    最近更新 更多