开发者

Why does a local variable lose its value when defining a method with define_method?

Trying to follow along with a metaprogramming screencast from pragpub and ran into some problems because of changes in Ruby since the release of screencast.

Hard to explain the problem w/o the code, so here's that:

class Discounter
  def discount(*skus)
    expensive_discount_calculation(*skus)
  end

  private

  def expensive_discount_calculation(*skus)
    puts "Expensive calculation for #{skus.inspect}"
    skus.inject {|m, n| m + n }
  end
end

def memoize(obj, method)
  ghost = class << obj; self; end
  ghost.class_eval do
    define_method(method) do |*args|
      memory ||= {}
      memory.has_key?(args) ? memory[args] : memory[args] = super(*args)
    end
  end
end

d = Discounter.new
memoize(d, :d开发者_运维知识库iscount)

puts d.discount(1,2,3)
puts d.discount(1,2,3)
puts d.discount(2,3,4)
puts d.discount(2,3,4)

Problem: The local variable in the method memorize should only change (by taking the return value from Discounter#discount) if it is being passed different arguments than it previously was.

For example I expect the output from running the code above to look like:

Expensive calculation for [1, 2, 3]
6
6
Expensive calculation for [2, 3, 4]
9
9

But this is the actual output:

Expensive calculation for [1, 2, 3]
6
Expensive calculation for [1, 2, 3]
6
Expensive calculation for [2, 3, 4]
9
Expensive calculation for [2, 3, 4]
9

Why isn't the local variable persisting across the calls? What am I missing to make this code work?

Thanks


If you define a local variable inside a block, it will vanish when the end of the block is reached.

To achieve the lifetime you want, you need to define the memory variable before the block:

def memoize(obj, method)
  memory = {}
  ghost = class << obj; self; end
  ghost.class_eval do
    define_method(method) do |*args|
      memory.has_key?(args) ? memory[args] : memory[args] = super(*args)
    end
  end
end
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜