Ruby:为什么 puts 调用 to_ary?

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

16

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

将给定对象写入ios,就像使用IO#print一样。在任何不已以换行符序列结尾的情况下,写入记录分隔符(通常是换行符)。如果使用数组参数调用,则将每个元素写入新行。

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

to_ary的内部使用在Ruby文档中真的没有很好地记录(Matz在他的《Ruby编程语言》书中指出了这一点)。

另一方面,printp方法只调用to_s,不调用to_ary

附注:有趣的是,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

谢谢。我认为本质是“在Ruby文档中,to_ary的内部使用并没有得到很好的记录”。我刚刚阅读了IO.puts文档,它们并没有明确提到to_ary,我认为这应该更清晰。感谢您指向《Ruby编程语言》一书,我可能会查看一下。 - Tom De Leu

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