在“What does it mean to use the name of a class for string interpolation?”中,Candide建议字符串中的#{}
隐式调用to_s
。
所以,例如:
my_array = [1, 2, 3, 4]
p my_array.to_s # => "[1, 2, 3, 4]"
p "#{my_array}" # => "[1, 2, 3, 4]"
但是,如果重新定义了to_s
数组,如下所示,我会得到不同的结果:
class Array
def to_s
self.map { |elem| elem.to_s }
end
end
p my_array.to_s # => ["1", "2", "3", "4"]
p "#{my_array}" # => "#<Array:0x007f74924c2bc0>"
我认为这种情况随时都会发生,无论如何to_s
都会被覆盖。
如果可能的话,我应该怎么做才能在字符串中保持to_s
和表达式#{}
之间的相等性?
我在RubyMonk lesson中遇到了这个问题:根据我的经验,根据课程#{ogres}
应该返回的内容是不同的。
答案 0 :(得分:4)
请查看Object#to_s
的文档。它说to_s
应该返回String
。覆盖方法时,应始终遵守其合同。看一下Array#to_s
的文档。正如您所看到的,还会返回String
。 [请注意,所有 to_X
和to_XYZ
方法都是如此:必须 总是返回一个对象对应的类,他们不得 raise
Exception
或以其他方式失败。]
但是,to_s
的实施确实 不会返回String
。它返回Array
,因此违反了 to_s
的合同。一旦违反方法合同,所有赌注都将被取消。就个人而言,我认为这里raise
TypeError
例外更合适,但Ruby正试图变得更好并返回一些 String
代替,在这种情况下)打印类名和一些唯一标识符。
以下是对RubySpec project的提交,其中(隐式)声明没有Exception
为raise
d并明确声明实现定义但未指定的String
是返回:The spec for interpolation when Object#to_s
did not return a String
was confusing the default representation of an arbitrary object and Object#inspect
.
在项目结束之前,规范的最新版本看起来像language/string_spec.rb#L197-L208
:
it "uses an internal representation when #to_s doesn't return a String" do obj = mock('to_s') obj.stub!(:to_s).and_return(42) # See rubyspec commit 787c132d by yugui. There is value in # ensuring that this behavior works. So rather than removing # this spec completely, the only thing that can be asserted # is that if you interpolate an object that fails to return # a String, you will still get a String and not raise an # exception. "#{obj}".should be_an_instance_of(String) end
正如您所看到的,在这种情况下保证的是,您赢得获得Exception
,并且您将得到一个String
,然而,它没有说明String
的样子。
答案 1 :(得分:2)
看看Ruby告诉你的事情:
"#{my_array}" # => "#<Array:0x007f74924c2bc0>"
这意味着Ruby看到了to_s
方法返回的数组,而不是Ruby期望的字符串,并且喜欢它,看看你是否没有覆盖原始的Array.to_s
。
而是使用类似的东西:
'[%s]' % self.map { |elem| elem.to_s }.join(', ')
更改您的代码以返回字符串,您就可以了。
考虑一下:
[].class # => Array
[].to_s.class # => String
class Array
def to_s
self.map { |elem| elem.to_s }
end
end
[].to_s.class # => Array
class Array
def to_s
'[%s]' % self.map { |elem| elem.to_s }.join(', ')
end
end
[].to_s.class # => String
my_array = [1, 2, 3, 4]
"#{my_array}" # => "[1, 2, 3, 4]"
在一般的实践中,我建议谨慎地覆盖核心和STD-Lib类&#39; to_s
因为他们正在做他们应该做的事情。对于自定义类,实现to_s
模仿与核心类相同的输出是个好主意。偶尔我们必须得到一些想象,并提供更详细的视图来了解对象的实例是什么样的,但是当我们覆盖inspect
时就是这样。
答案 2 :(得分:1)
您对var cursor = Collection.find();
cursor.forEach(function(doc){
console.log(doc._id);
// fill new object here...
});
的定义会返回一个数组,需要进一步应用Array#to_s
。这将导致无限递归。我怀疑Ruby有内部实现,以便在to_s
的情况下切断这种无限递归。对于"#{my_array}"
,p my_array.to_s
是一个数组,而my_array.to_s
适用p
,这不会导致无限递归。