关于在Ruby中传递参数的问题

时间:2011-07-20 07:35:00

标签: ruby metaprogramming

比较以下两个代码段:

class Logger 
  def self.add_logging(id_string)
    define_method(:log) do |msg| 
      now = Time.now.strftime("%H:%M:%S")
      STDERR.puts "#{now}-#{id_string}: #{self} (#{msg})"
    end 
  end
end 

class Song < Logger
  add_logging "Tune" 
end

song = Song.new 
song.log("rock on")

class Logger
  def self.add_logging(id_string)
    def log(msg)
      now = Time.now.strftime("%m")
      puts "#{now}-#{id_string}: #{self}(#{msg})"
    end
  end
end

class Song < Logger
  add_logging "Tune"
end

s = Song.new

s.log("can't smile with you")
#=> NameError: undefined local variable or method `id_string' for #<Song:0x000001018aad70>

我无法弄清楚为什么第二种情况会出现NameError错误,以及无法将id_string传递给它的原因。

4 个答案:

答案 0 :(得分:3)

def创建一个新范围;一个街区没有。新范围会切断周围变量的可见性。 ruby还有另外两个“新范围创建者”:类和模块。

x = 10

3.times do |i|
  puts i * x
end

def do_stuff
  puts x * 10
end

do_stuff  

--output:--
0
10
20
`do_stuff': undefined local variable or method `x' 

答案 1 :(得分:1)

id_string是方法add_logging的本地。在后一种实现中,log-method无法看到它,因此出现错误。在前一个实现中,您可以在 add_logging中动态定义log-method

换句话说,局部变量在其定义的范围内是可见的(在本例中是一种方法)。在后一个实现中,您有嵌套的作用域(=方法中的方法声明),而内部作用域不能访问外部作用域的本地变量。

正如@stef在回答中所建议的那样,你可以解决这个我扩大变量范围的问题。我建议尽量保持变量范围“紧”,因此更喜欢你的第一个实现。

答案 2 :(得分:0)

尝试使用类变量吗?

class Logger 
  def self.add_logging(id_string)
    @@my_id = id_string
    define_method(:log) do |msg| 
      now = Time.now.strftime("%H:%M:%S")
      STDERR.puts "#{now}-#{@@my_id}: #{self} (#{msg})"
    end 
  end
end 

答案 3 :(得分:0)

由于红宝石存在问题,因此应避免使用类变量。 ruby方式是改为使用“类实例变量”。