Ruby:为什么要调用to_ary?

Tom*_*Leu 13 ruby metaprogramming method-missing

我正在学习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
Run Code Online (Sandbox Code Playgroud)

现在,这段代码:

x = X.new

x.some_method
puts
x.some_method
puts
puts x
Run Code Online (Sandbox Code Playgroud)

产生输出:

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>
Run Code Online (Sandbox Code Playgroud)

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

我用Google搜索并发现了以下相关链接:

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

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

def to_s
  "I'm an instance of X"
end
Run Code Online (Sandbox Code Playgroud)

然后输出"puts x":

method to_ary not found. Defining it.
defined method to_ary
I'm an instance of X
Run Code Online (Sandbox Code Playgroud)

Ali*_*kau 16

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

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

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

to_ary内部使用在Ruby文档中没有很好的记录(Matz在他的The Ruby Programming Language一书中指出了这一点).

方法printp另一方面不要打电话to_ary,只to_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
Run Code Online (Sandbox Code Playgroud)