Ruby 中的 to_s 与 to_str(以及 to_i/to_a/to_h 与 to_int/to_ary/to_hash)

Posted

技术标签:

【中文标题】Ruby 中的 to_s 与 to_str(以及 to_i/to_a/to_h 与 to_int/to_ary/to_hash)【英文标题】:to_s vs. to_str (and to_i/to_a/to_h vs. to_int/to_ary/to_hash) in Ruby 【发布时间】:2012-06-26 06:41:13 【问题描述】:

我正在学习 Ruby,我看到了一些让我有点困惑的方法,特别是 to_sto_str(同样,to_i/to_intto_a/@987654326 @, & to_h/to_hash)。我读到的内容解释了较短的形式(例如to_s)用于显式转换,而较长的形式用于隐式转换。

我真的不明白to_str 的实际使用方式。字符串以外的东西会定义to_str吗?你能给出这个方法的实际应用吗?

【问题讨论】:

【参考方案1】:

首先请注意,所有这些都适用于每对“短”(例如to_s/to_i/to_a/to_h)与“长”(例如to_str/to_int/ to_ary/to_hash) Ruby 中的强制方法(针对它们各自的类型),因为它们都具有相同的语义。


它们有不同的含义。你不应该实现to_str,除非你的对象行为像一个字符串,而不是仅仅可表示一个字符串。实现to_str 的唯一核心类是String 本身。

来自Programming Ruby(引自this blog post,值得一读):

[to_ito_s] 并不是特别严格:例如,如果一个对象具有某种体面的字符串表示形式,它可能会有一个 to_s 方法... [to_int 和 @987654340 @] 是严格的转换函数:只有当 [your] 对象可以自然地在每个可以使用字符串或整数的地方使用时,您才能实现它们。

Older Ruby documentation from the Pickaxe 有话要说:

与几乎所有类都支持的to_s 不同,to_str 通常只由那些行为类似于字符串的类实现。

例如,除了Integer,Float 和Numeric 都实现了to_intto_i 等效于to_str),因为它们都可以很容易地替换一个整数(它们是所有实际数字)。除非你的类与 String 有同样紧密的关系,否则你不应该实现 to_str

【讨论】:

安德鲁,谢谢。但在实际意义上,“那些行为像字符串的类”是什么意思?这是否意味着它们实现了与字符串相同的方法?如果对象已经像字符串一样,to_str 除了 self 还会返回什么? @JeffStorey 是的。除非你的类可以被字符串替换,否则你不应该实现to_str。 String 对to_sto_str 的实现就是这样做的:返回它们自己。实现to_str 是一种验证您的行为是否像字符串一样的方法。许多核心方法以这种方式使用to_str。我引用的博文值得一读:) 安德鲁,谢谢。我确实看过那篇文章,但我仍然有点不清楚。有没有可能实现 to_str 的时候?那是我真正感到困惑的部分......我想不出一个实际的例子。 @JeffStorey 除非您正在创建一个类似于 String 的类并且至少实现它所做的所有方法,否则不会。当然,在这种情况下,无论如何您都可能拥有 String 子类,并且可以免费获得它。何时这样做的一个很好的例子是 Ruby 的数字:Float、Integer 和 Numeric 都实现了to_intto_i 的“to_str”等效项),因为它们可以很容易地相互替换。 查看 Ruby koans about_to_str 示例以获得更多信息。【参考方案2】:

要了解是否应该使用/实现to_s/to_str,让我们看一些示例。考虑这些方法何时失败是很有启发性的。

1.to_s              # returns "1"
Object.new.to_s     # returns "#<Object:0x4932990>"
1.to_str            # raises NoMethodError
Object.new.to_str   # raises NoMethodError

正如我们所见,to_s 很乐意将任何对象 转换为字符串。另一方面,to_str 在其参数看起来不像字符串时引发错误


现在让我们看看Array#join

[1,2].join(',')     # returns "1,2"
[1,2].join(3)       # fails, the argument does not look like a valid separator.

Array#join 在加入数组之前将它们转换为字符串(无论它们实际上是什么)是很有用的,因此Array#join 对它们调用to_s

但是,分隔符应该是一个字符串 -- 调用[1,2].join(3) 的人可能会出错。这就是Array#join 在分隔符上调用to_str 的原因。


同样的原则似乎也适用于其他方法。在散列上考虑to_a/to_ary

1,2.to_a      # returns [[1, 2]], an array that describes the hash
1,2.to_ary    # fails, because a hash is not really an array.

总之,我是这样看的:

调用to_s 获取描述对象的字符串。 调用to_str 来验证对象是否真的像字符串一样。 当您可以构建一个描述您的对象的字符串时,实现to_s。 当您的对象可以完全像字符串一样运行时,实现to_str

我认为你可以自己实现to_str 的情况可能是ColoredString 类——一个带有颜色的字符串。如果您似乎很清楚将彩色逗号传递给 join 不是错误并且应该导致 "1,2"(即使该字符串不会被着色),那么 do 实现 to_str在彩色字符串上。

【讨论】:

【参考方案3】:

Zverok 有一篇很好理解的文章,关于何时使用什么(用 to_h 和 to_hash 解释)。

你的实现这些方法的对象是否可以转换为字符串 -> 使用to_s 或者它是某种(增强的)字符串的类型 -> 使用to_str


我在 gem 'configuration'(GitHub 和 Configuration.rb)中的 Configuration 类的实践中看到了 to_hash 的有意义用法

它代表——顾名思义——提供的配置,实际上是一种哈希(具有附加功能),而不是可以转换为一个。

【讨论】:

以上是关于Ruby 中的 to_s 与 to_str(以及 to_i/to_a/to_h 与 to_int/to_ary/to_hash)的主要内容,如果未能解决你的问题,请参考以下文章

如何反转 Hash.inspect 或 Array.inspect? (又名 .to_s)在 Ruby 中

将 + 号放在 ruby​​ 中的正数前面

Ruby 学习笔记2

to_string 不是 std 的成员,g++ (mingw) 说

ruby 动态生成变量

计算字符在Ruby中的字符串数组中出现的次数