茱莉亚记忆化

4
在Mathematica中,如果您想让一个函数记住它的值,语法上非常简单。例如,这是标准示例 - 斐波那契数列:
fib[1] = 1
fib[2] = 1
fib[n_]:= fib[n] = fib[n-1] + fib[n-2]

有没有一种在Julia中做到这一点的语法上更为简洁的方式?其中一个让它变得更加复杂的部分是类型系统-这里是一个针对单个未分类参数的实现:
function memoizeany(func)
    thedict = Dict()
    return (a)-> memoizeanyaux(a, func, thedict)
end

function memoizeanyaux(a, func, thedict)
    if haskey(thedict, a)
        return thedict[a]
    end
    res = func(a)
    thedict[a] =res
    return res
end

对于每个类型签名都这样做似乎有些痛苦,而最好的Julia方式是使用@memoize宏来实现,但这并不能真正回答问题。肯定以前遇到过这种情况。


2
相关包:https://github.com/colinfang/Memoize.jl - Gnimuc
1
@Gnimuc 谢谢!实际上我在问问题后找到了它,但似乎没有解决主要问题(它使用了一个未经类型定义的字典,在快速浏览后)。 - Igor Rivin
我刚发现了另一个 Memoize 库:https://github.com/colinfang/Memoize.jl(*类型推断友好的通用函数记忆化*),但我自己还没有尝试过。 - HarmonicaMuse
1
这里有一个已注册的软件包,但缺乏推理友好性:https://github.com/simonster/Memoize.jl - HarmonicaMuse
@SalchiPapa 很棒!我会去看看。 - Igor Rivin
在 JuliaCollections 中:https://github.com/JuliaCollections/Memoize.jl - ederag
1个回答

7

当我需要使用使用持久内存的函数时,我会简单地创建struct并重载call符号使它们成为函数对象。对于您提供的示例,我将写出斐波那契数列:

struct MyFib{T<:Real}
  n::Vector{T}

  function (::Type{MyFib})(::Type{T} = Int) where T
    new{T}([0, 1])
  end

  function (m::MyFib{T})() where T
    result = sum(m.n)
    m.n .= [result, m.n[1]]
    return result
  end
end

m = MyFib()

for n in 1:10
  @show n, m()
end

这种使用struct的方式对我来说足够通用,可以封装我在计算中所需的任何内容。此外,由于它是使用参数T进行严格类型化的,因此也很高效。
然后,对于上面的示例,您还可以使用一些参数化函数和函数递归,这将有望从v0.7中的整数常量传播中受益,并变得与其在C++中的模板对应项一样高效。
myfib(::Val{0}) = 0
myfib(::Val{1}) = 1

function myfib(::Val{N})::Int where N
  return myfib(Val{N-1}()) + myfib(Val{N-2}())
end

# or, as a single liner, as per crstnbr's comment,
myfib(::Val{N}) where N = myfib(Val{N-1}()) + myfib(Val{N-2}())

# and, with some syntactic sugar, thanks to SalchiPapa,
myfib(n::Int) = myfib(Val{n}())

for n in 1:10
  @show n, myfib(n)
end

相比于Mathematica版本,我认为后者更适合你的例子。但它不像前者那样通用,因为它是一种编译时技术,所以可能会给编译器带来一定压力。


1
为什么不直接使用myfib(::Val{N}) where N = myfib(Val{N-1}()) + myfib(Val{N-2}()),为什么这么冗长? - carstenbauer
当然,我们可以这样编写。但这并不会改变 julia 解释和编译的方式。让我修改回答。 - Arda Aytekin
谢谢!这非常有启发性 - 非常感谢! - Igor Rivin
不客气,@IgorRivin。很高兴能帮到你... 干杯! :) - Arda Aytekin
1
你还可以为最后一个例子添加另一种方法 myfib(n::T) where {T<:Int} = myfib(Val{n}()),这样用户现在只需要输入 myfib(n) 而不是 myfib(Val{n}()) - HarmonicaMuse
1
@SalchiPapa,在这种情况下,我宁愿使用myfib(n::Int),因为isempty(subtypes(Int)) == true - Arda Aytekin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接