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)
。