Ruby中关于参数传递的问题

3

比较以下两段代码:

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个回答

3

def创建一个新的作用域;块不会。新的作用域切断了周围变量的可见性。Ruby还有另外两个“新作用域创建者”:class和module。

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

id_string 是 add_logging 方法的局部变量。在您后来的实现中,log-method 看不到它,因此出现了错误。在前面的实现中,您动态定义了 add_logging 中的 log-method。

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

正如 @stef 的答案所建议的那样,您可以通过扩大变量的作用域来解决这个问题。我建议尽可能保持变量作用域“紧凑”,因此更喜欢您的第一个实现。


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 

0

由于问题的性质,Ruby应该避免使用类变量。 Ruby推荐使用“类实例变量”。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接