我可以通过使用本地范围和闭包来记忆Ruby函数:
require "benchmark"
fib = ->(n) {
return 0 if n < 0
return 1 if n == 0
return fib.(n-1) + fib.(n-2)
}
memoize = ->(f) {
already_know = {}
new_function = ->(n) {
already_know[n] ||= f.(n)
return already_know[n]
}
return new_function
}
fib = memoize.(fib)
puts Benchmark.measure { p fib.(42) }
并且需要0.000011秒才能运行。如果没有行fib = memoize.(fib)
,则需要259秒才能运行。
但是你可以用Ruby中的方法(而不是函数)做同样的事情吗?看起来Python方法更像是一个函数,因为你可以使用Python方法轻松实现,而Ruby的方法不像函数 - 也许是因为Ruby中的方法不是对象。但问题是,您是否可以执行上述代码中的memoize
之类的操作来使方法成为备忘录?
答案 0 :(得分:2)
您可以为原始方法添加别名,并为原始实现添加简单缓存。
class Fib
def fib(n)
case
when n < 0 then 0
when n == 0 then 1
else fib(n-1) + fib(n-2)
end
end
end
puts Benchmark.measure { p Fib.new.fib(35) }
class Fib
alias_method :fib_ori, :fib
def fib(n)
(@fib_cache ||= {})[n] ||= fib_ori(n)
end
end
puts Benchmark.measure { p Fib.new.fib(35) }
答案 1 :(得分:1)
与sschmeck's solution类似,但是通过继承:
class Fibonacci
def at(n)
return 0 if n < 0
return 1 if n == 0
return at(n - 1) + at(n - 2)
end
end
class MemoizedFibonacci < Fibonacci
def initialize
@memo = {}
end
def at(n)
@memo[n] ||= super
end
end
答案 2 :(得分:1)
Module#prepend
在Ruby 2+中被特别添加,因为它可以(除其他外)充当类似于CLOS或Python的方法组合器/装饰器。这样,您实际上不需要访问方法本身,您可以覆盖它。
class Module
def memoize(meth)
prepend(Module.new do
memo = {}
define_method(meth) do |*args, &blk|
memo[[self, *args, blk]] ||= super(*args, &blk)
end
end)
end
end
class Integer
memoize def fib
raise ArgumentError if self < 0
return self if self < 2
pred.fib + pred.pred.fib
end
end
require 'benchmark'
puts Benchmark.measure { p 42.fib }
在旧版本的Ruby(1.9或更早版本)中,您必须执行以下操作:
class Module
def memoize(meth)
memo = {}
old_meth = instance_method(meth)
define_method(meth) do |*args, &blk|
memo[[self, *args, blk]] ||= old_meth.bind(self).(*args, &blk)
end
end
end
此外,在Ruby 2.2中添加了def
评估表示所定义方法名称的Symbol
,因此,在旧版本中,您必须改为:
class Integer
def fib
raise ArgumentError if self < 0
return self if self < 2
pred.fib + pred.pred.fib
end
memoize :fib
end
我们可以使用诸如Rake用于其desc
方法的技巧,以使其记住下一个定义的方法:
class Module
def memoize(meth=nil)
return @__memoize_next_method__ = true unless meth
memo = {}
old_meth = instance_method(meth)
define_method(meth) do |*args, &blk|
memo[[self, *args, blk]] ||= old_meth.bind(self).(*args, &blk)
end
end
def method_added(meth)
return if @__recursing__
@__recursing__ = true # protect against infinite recursion
if @__memoize_next_method__
memoize(meth)
@__memoize_next_method__ = nil
end
@recursing = nil
end
end
class Integer
memoize
def fib
raise ArgumentError if self < 0
return self if self < 2
pred.fib + pred.pred.fib
end
end