13

我正在学习 Ruby 中的元编程,并且正在尝试通过 method_missing 和 define_method 定义缺失的方法。我遇到了一些意想不到的行为,想知道是否有人可以解释这一点。这是我的课:

class X
  def method_missing(m, *args, &block)
    puts "method #{m} not found. Defining it."
    self.class.send :define_method, m do
      puts "hi from method #{m}"
    end
    puts "defined method #{m}"
  end  
end

现在,这段代码:

x = X.new

x.some_method
puts
x.some_method
puts
puts x

产生输出:

method some_method not found. Defining it.
defined method some_method

hi from method some_method

method to_ary not found. Defining it.
defined method to_ary
#<X:0x007fcbc38e5030>

我没有得到的是最后一部分:为什么 Ruby 在调用 puts 时调用 to_ary?为什么 Ruby 会尝试将我的对象转换为数组来打印它?

我用谷歌搜索并找到了这些相关链接:

这些也讨论了 method_missing 和 to_ary 陷阱,但没有具体讨论为什么 puts 会调用 to_ary。

我还应该提到,当我定义一个 to_s 时,行为不会改变,例如

def to_s
  "I'm an instance of X"
end

“puts x”的输出是:

method to_ary not found. Defining it.
defined method to_ary
I'm an instance of X
4

1 回答 1

16

puts是 的同义词$stdout.puts。$stdout 是一个IO类,因此请查看IO.puts的文档:

与 IO#print 一样将给定对象写入 ios。在尚未以换行序列结尾的任何内容之后写入记录分隔符(通常是换行符)。如果使用数组参数调用,则将每个元素写入新行。

这意味着该puts方法旨在编写多行输出。因此,它尝试调用to_ary对象的方法,如果to_ary定义了,则Array在新行上打印返回的每个元素,否则puts调用to_s方法。

to_aryRuby 文档中确实没有很好地记录内部用法(Matz 在他的 The Ruby Programming Language书中指出了这一点)。

另一方面,方法不调用print,只有.pto_aryto_s

旁注:有趣,to_ary必须返回真实Array对象,而不是对象定义each方法或其他东西:

class Test
  def to_ary
    10.downto(1)
  end
end

puts Test.new

#TypeError: can't convert Test to Array (Test#to_ary gives Enumerator)
#        from (irb):28:in `puts'
#        from (irb):28:in `puts'
#        from (irb):28
于 2012-01-22T11:44:20.873 回答