朱莉娅记忆
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
这种使用 struct
s 的方式非常通用,足以让我在计算中封装我需要的任何东西。另外,因为它是使用参数 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 技术。
在 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
这种使用 struct
s 的方式非常通用,足以让我在计算中封装我需要的任何东西。另外,因为它是使用参数 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 技术。