Ruby 将块传递到define_方法 问题

Ruby 将块传递到define_方法 问题,ruby,memoization,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 f

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

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
我想把这个弄干,看起来像这样:

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
为此,我定义了一个模块
attracculator
,以扩展到
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
嗯,这很奇怪。为什么
block
InfoGatherer:Class
的范围内运行,而不是在其实例
InfoGatherer.new
中运行

研究 我知道我不能使用
yield
,因为那样会试图捕获错误的块,如图所示。 我试图用
self.instance\u exec(block)
代替上面的
block.call
,但后来我收到一个新错误:

LocalJumpError: no block given
嗯?我在中看到了相同的错误,但我已经使用了括号表示法,因此那里的答案似乎不适用

我还尝试使用
class\u eval
,但我不确定如何在字符串内部调用
block
。这当然行不通:

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

这个用例被称为记忆。这很容易做到,如:

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

你不应该像现在这样陷入混乱。

来详述最后一个人

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

class ComplexCalculation

  def initialize(variable)
    @variable = variable
  end

  def evaluate(variable)
    #stuff
  end

end

通过提取这个类,您将隔离这种复杂性,并将获得更好的体验

问题在于,在
define_方法
中,
self
出乎意料地是
InfoGatherer
,而不是
InfoGatherer
的实例。因此,我使用
self.instance\u exec(block)
的方法是正确的

工作解决方案是
self.instance\u exec(&block)
(注意符号)。我猜解释器不会认识到
block
是块,除非您将其标记为块?如果有人能比我解释得更好,请解释


作为旁注,这不是解决这个特殊问题的最佳方法。请参阅@sawa的答案,以获得一种清晰的方法来记忆复杂的计算。

这就是我在进行简短计算时所做的。:)但你是对的;任何复杂的计算都可以移动到辅助方法中,并轻松地放在一行上。不过,我仍然很好奇,将块传递到
define\u方法中的正确方法是什么。即使我提供的用例不是一个可靠的用例,它也不必在一行中。如果您有一长段代码,您可以执行
| |=开始。。。结束
。指出这一点很好,但这只是众多选项中的一个,包括
@people | |=my_方法(变量)
。顺便说一句,我想你的意思是
def people(variable=nil)