朱莉娅记忆

Julia memo-izing

Mathematica 中,如果你想让一个函数记住它的值,从语法上来说是很容易的。例如,这里是标准示例 - Fibonacci:

fib[1] = 1
fib[2] = 1
fib[n_]:= fib[n] = fib[n-1] + fib[n-2]

在 Julia 中是否有一些语法上令人愉快的方式来做到这一点?它使它更复杂的部分原因是类型系统 - 这是单个未类型化 arg 的实现:

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 宏,但这实际上并不能回答问题。这肯定以前出现过。

当我需要使用使用持久内存的函数时,我只需创建 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

这种使用 structs 的方式非常通用,足以让我在计算中封装我需要的任何东西。另外,因为它是使用参数 T 严格类型化的,所以它也很高效。

然后,对于上面的示例,您还可以使用一些参数函数和函数递归,它们有望从 v0.7 中的整数常量传播中获益,从而获得与 [=17] 中的模板化副本一样的效率=]:

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 版本相比,后者对您的示例更友好。但它不像前者那样通用,而且你会给编译器带来一些压力,因为它是一种 compile-time 技术。