Ruby:为什么 put 调用 to_ary?

发布于 2024-12-28 02:44:55 字数 1444 浏览 2 评论 0 原文

我正在学习 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 在调用 put 时调用 to_ary?为什么 Ruby 会尝试将我的对象转换为数组只是为了打印它?

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

这些还讨论了 method_missing 和 to_ary 陷阱,但没有具体讨论为什么 put 会调用 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

I'm learning metaprogramming in Ruby and am just trying out defining missing methods via method_missing and define_method. I'm getting some unexpected behaviour and am wondering if anyone can explain this. Here is my class:

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

Now, this code:

x = X.new

x.some_method
puts
x.some_method
puts
puts x

Produces the output:

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>

What I don't get is the last part: why is Ruby calling to_ary in a call to puts? Why would Ruby try to convert my object into an array just to print it?

I've Googled around and found these related links:

These also talk about method_missing and to_ary gotchas, but not specifically about why puts would call to_ary.

I should also mention that the behaviour does not change when I define a to_s, e.g.

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

The output of "puts x" is then:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

花之痕靓丽 2025-01-04 02:44:55

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

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

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

Ruby 文档中确实没有很好地记录 to_ary 内部用法(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

puts is a synonym for $stdout.puts. $stdout is an IO class, so look at the documentation for IO.puts:

Writes the given objects to ios as with IO#print. Writes a record
separator (typically a newline) after any that do not already end with
a newline sequence. If called with an array argument, writes each
element on a new line.

This mean that puts method is intended to write several lines of output. Thus it tries to call to_ary method on an object and if to_ary is defined, then prints each element of the returned Array on a new line, else puts calls to_s method.

to_ary internal usage is really not well documented in the Ruby documentation (Matz points this out in his The Ruby Programming Language book).

Methods print and p on the other hand don't call to_ary, only to_s.

Sidenote: Interesting, that to_ary must return real Array object, not an object defining each method or something else:

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文