我很好奇为什么 ruby 返回一个 Enumerator 而不是一个 Array ,因为 Array 似乎是一个明显的选择。例如:
'foo'.class
# => String
大多数人认为 String 是一个字符数组。
'foo'.chars.class
# => Enumerator
那么为什么 String#chars 返回一个 Enumerable 而不是一个数组呢?我假设有人对此进行了很多思考并决定 Enumerator 更合适,但我不明白为什么。
我很好奇为什么 ruby 返回一个 Enumerator 而不是一个 Array ,因为 Array 似乎是一个明显的选择。例如:
'foo'.class
# => String
大多数人认为 String 是一个字符数组。
'foo'.chars.class
# => Enumerator
那么为什么 String#chars 返回一个 Enumerable 而不是一个数组呢?我假设有人对此进行了很多思考并决定 Enumerator 更合适,但我不明白为什么。
如果你想要一个数组,调用#to_a
. Enumerable
和之间的区别在于Array
,一个是懒惰的,另一个是渴望的。这是良好的旧内存(懒惰)与 cpu(渴望)优化。显然他们选择了懒惰,也是因为
str = "foobar"
chrs = str.chars
chrs.to_a # => ["f", "o", "o", "b", "a", "r"]
str.sub!('r', 'z')
chrs.to_a # => ["f", "o", "o", "b", "a", "z"]
抽象 - 某些东西可能是 Array 的事实是您在许多用例中不关心的实现细节。对于那些你这样做的人,你总是可以调用.to_a
Enumerable 来获得一个。
效率 - 枚举器是懒惰的,因为 Ruby 不必一次构建整个元素列表,而是可以根据需要一次构建一个。因此,实际上只计算了您需要的数字。当然,这会导致每个项目的开销增加,所以这是一个权衡。
可扩展性 -chars
返回 Enumerable 的原因是因为它本身是作为枚举器实现的;如果您将一个块传递给它,该块将每个字符执行一次。这意味着不需要 eg .chars.each do ... end
; 你可以做.chars do ... end
。这使得在字符串的字符上构造操作链变得容易。
这完全符合 1.9 的精神:尽可能返回枚举数。String#bytes、String#lines、String#codepoints,还有像 Array#permutation 这样的方法都返回一个枚举器。
在 ruby 1.8 String#to_a 中产生了一系列行,但该方法在 1.9 中消失了。
'大多数人认为字符串是一个字符数组'......只有当你像 C 或其他语言一样思考时。恕我直言,Ruby 的面向对象比这要先进得多。大多数Array
操作往往Enumerable
更像,所以这样可能更有意义。
数组非常适合随机访问不同的索引,但字符串很少被特定索引访问。(如果您尝试访问特定索引,我怀疑您可能正在做学业)
如果您尝试检查每个字符,则 Enumerable 有效。使用 Enumberable,您可以访问map
、each
、inject
等。对于替换,还有字符串函数和正则表达式。
坦率地说,我想不出现实世界需要一系列字符。
也许string
红宝石中的 a 是可变的?然后拥有一个Array
并不是一个明显的选择——例如,长度可能会改变。但是你仍然想列举字符......
此外,您真的不想传递字符串字符的实际存储空间,对吧?我的意思是,我不记得太多 ruby(已经有一段时间了),但如果我正在设计界面,我只会分发.chars
方法/属性/其他内容的“副本”。现在...你想每次都分配一个新数组吗?或者只是返回一个知道如何枚举字符串中字符的小对象?因此,保持隐藏的实现。
所以不行。大多数人不认为字符串是一个字符数组。大多数人认为字符串是字符串。具有由库/语言/运行时定义的行为。有了一个实现,你只需要知道什么时候你想用抽象带以下的东西变得讨厌和私有。
实际上'foo'.chars将 str 中的每个字符传递给给定的块,如果没有给出块,则返回一个枚举器。
核实 :
irb(main):017:0> 'foo'.chars
=> #<Enumerable::Enumerator:0xc8ab35 @__args__=[], @__object__="foo", @__method__=:chars>
irb(main):018:0> 'foo'.chars.each {|p| puts p}
f
o
o
=> "foo"