define_method的一些问题

时间:2014-01-01 10:51:43

标签: ruby metaprogramming

所以我正在尝试使用此代码,

class Colors
  def initialize color
    color.each {|c| color c}
  end

  def color c
    self.class.send(:define_method, "about_#{c}") do
      puts "The color #{c} has an intensity of  #{Random.rand}"
    end
  end
end

a = Colors.new(["orange", "pink", "yellow", "green"])
a.about_pink
a.about_pink
a.about_pink

在我的机器上,我得到:

The color pink has an intensity of  0.377090691263002
The color pink has an intensity of  0.8375972769713161
The color pink has an intensity of  0.26820920750202837
问题是打印出4个不同编号的语句。不应该打印的所有语句都包含相同的随机数,因为该方法只被“定义”一次吗?

3 个答案:

答案 0 :(得分:3)

您要做的是在您定义方法时评估Random。这样,对于定义的方法,该值是固定的:

class Colors
  def initialize color
    @int = {}
    color.each {|c| color c}
  end

  def color c
    intensity = Random.rand
    self.class.send(:define_method, "about_#{c}") do
      puts "The color #{c} has an intensity of #{intensity}"
    end
  end
end

a = Colors.new(["orange", "pink", "yellow", "green"])
a.about_pink
a.about_pink
a.about_pink

如您所见,我将Random的结果保存在变量中,该变量在内部上下文中得到修复。在您的初始示例中发生的情况是,您输出的字符串将在每次调用中进行评估,并且该评估在其中调用Random,因此每次都会运行它。

答案 1 :(得分:1)

由于您始终在块内调用Random.rand,因此值始终不同,因为块代码也始终被调用。所以你的结果是正确的。但要在第一时间保留该值并重用它,只需将其存储到var中,如下所示:

  def initialize color
    @int = {}
    color.each {|c| color c}        
  end

  def color c
    self.class.send(:define_method, "about_#{c}") do
      puts "The color #{c} has an intensity of #{@int[ c ] ||= Random.rand}"
    end
  end

我们会得到类似的东西:

The color pink has an intensity of 0.6879417562602181
The color pink has an intensity of 0.6879417562602181
The color pink has an intensity of 0.6879417562602181
The color orange has an intensity of  0.8019817000526268

答案 2 :(得分:1)

没有。评估块的时间由使用它的方法确定。某些方法(例如tap)会立即对其进行评估,而其他方法(例如[].each)则永远不会对其进行评估。在您的情况下,send不对块执行任何操作,并将其传递给define_method,这会将块转换为所定义方法的方法体。这意味着每次调用定义的方法时都会对块进行评估。