将块传递给define_method

时间:2015-01-10 03:08:38

标签: ruby memoization

问题

有一种模式我发现自己经常使用,所以我想把它弄干。我有这样的东西:

class InfoGatherer
  def foo
    true
  end

  def people
    unless @people
      @people = # Long and complex calculation (using foo)
    end
    @people
  end
end

我想干这个看起来像这样:

class InfoGatherer
  extend AttrCalculator

  def foo
    true
  end

  attr_calculator(:people) { # Long and complex calculation (using foo) }
end

为实现这一目标,我将模块AttrCalculator定义为InfoGatherer。这就是我的尝试:

module AttrCalculator
  def attr_calculator(variable_name_symbol)
    variable_name = "@#{variable_name_symbol}"

    define_method variable_name_symbol do
      unless instance_variable_defined?(variable_name)
        instance_variable_set(variable_name, block.call)
      end
      instance_variable_get(variable_name)
    end

  end
end

不幸的是,当我尝试像InfoGatherer.new.people这样简单的事情时,我得到了:

NameError: undefined local variable or method `foo' for InfoGatherer:Class

嗯,这很奇怪。为什么blockInfoGatherer:Class范围内运行,而不是在其InfoGatherer.new实例中运行?

研究

我知道我无法使用yield,因为这会尝试捕获错误的区块,如here所示。 我尝试在上面self.instance_exec(block)的位置使用block.call,但之后又收到了一个新错误:

LocalJumpError: no block given

咦?我在this SO question中看到了同样的错误,但我已经使用了括号表示法,所以答案似乎并不适用。

我也尝试使用class_eval,但我不确定如何在字符串中调用block。这肯定不起作用:

class_eval("
  def #{variable_name_symbol}
    unless #{variable_name}
      #{variable_name} = #{block.call}
    end
    #{variable_name}
  end
")

3 个答案:

答案 0 :(得分:4)

该用例称为memoization。它可以很容易地完成:

def people
  @people ||= # Long and complex calculation (using foo)
end

你不应该像你一样陷入困境。

答案 1 :(得分:1)

扩展最后的人

def people(varariable = nil)
  @people ||= ComplexCalculation.new(variable).evaluate
end

class ComplexCalculation

  def initialize(variable)
    @variable = variable
  end

  def evaluate(variable)
    #stuff
  end

end

通过提取此类,您可以隔离这种复杂性并获得更好的体验。

答案 2 :(得分:1)

问题是,在define_method内,self出人意料InfoGatherer,而不是InfoGatherer实例。所以我和self.instance_exec(block)走在了正确的轨道上。

工作解决方案是self.instance_exec(&block)(请注意&符号)。我想解释器并不认识block是一个块,除非你将它标记为?如果有人能比我更好地解释这一点,请做。

作为旁注,这是不是解决此特定问题的最佳方法。请参阅@ sawa的答案,了解记忆复杂计算的简洁方法。