【问题标题】:Why return an enumerator?为什么要返回枚举数?
【发布时间】:2012-05-17 22:42:09
【问题描述】:

我很好奇为什么 ruby​​ 会返回 Enumerator 而不是 Array,因为 Array 似乎是一个显而易见的选择。例如:

'foo'.class
# => String

大多数人认为 String 是一个字符数组。

'foo'.chars.class
# => Enumerator

那么为什么 String#chars 返回的是 Enumerable 而不是 Array?我假设有人对此进行了深思熟虑,并认为 Enumerator 更合适,但我不明白为什么。

【问题讨论】:

  • 您需要一个字符数组用于什么应用程序?也许令人惊讶的是因为您没有以红宝石的方式解决问题......
  • @DGM - 例如,假设我想在字符串中列出唯一字符,这样做会很好:'foo'.chars.uniq 而不是 'foo'.chars.to_a.uniq .我的问题是:强制执行额外的步骤有什么真正的优势吗?
  • @pguardiario(或其他具有编辑能力的用户)请将文本中的另一个 Enumerable 替换为 Enumerator。
  • @gorn - Enumerator 是一个包含 Enumerable mixin 的类。我们说的是同一件事。
  • @pguardiario 这没问题,但是在“那么为什么 String#chars 返回一个 Enumerable ...”这句话中,这是无稽之谈。 String#chars 返回 Enumerator not Enumerable。或者你可以说。 String#chars 返回其类包含 Enumerable 的对象。没关系,但不是很简单。

标签: ruby arrays enumerator


【解决方案1】:

也许 ruby​​ 中的 string 是可变的?然后拥有Array 并不是一个明显的选择——例如,长度可能会改变。但是你还是要枚举字符...

另外,您并不想为字符串的字符传递 actual 存储,对吗?我的意思是,我不记得多少 ruby​​(已经有一段时间了),但如果我正在设计界面,我只会分发 .chars 方法/属性/任何东西的“副本”。现在...你想每次都分配一个新数组吗?或者只是返回一个知道如何枚举字符串中字符的小对象?因此,隐藏实现。

所以,不。大多数人不认为字符串是一个字符数组。大多数人认为字符串是字符串。具有由库/语言/运行时定义的行为。有了一个实现,你只需要知道什么时候你想用抽象带以下的东西变得讨厌和私有。

【讨论】:

    【解决方案2】:

    如果您想要一个数组,请致电#to_aEnumerableArray 的区别在于一个是懒惰的,另一个是急切的。这是良好的旧内存(懒惰)与 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"]
    

    【讨论】:

    • 我喜欢这种解释,但我不确定我是否能想象这样一种情况,即我会调用 String#chars 而不会以不会产生额外开销的方式使用它阵列已经。
    【解决方案3】:

    实际上 '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"
    

    【讨论】:

    • 嗯,我相信这是您正在考虑的 Enumerable#each。
    【解决方案4】:
    1. 抽象 - 某些可能是数组的事实是您在许多用例中不关心的实现细节。对于那些你这样做的人,你可以随时在 Enumerable 上调用.to_a 来获取一个。

    2. 效率 - 枚举器是惰性的,因为 Ruby 不必一次构建整个元素列表,但可以根据需要一次构建一个。因此,实际上只计算了您需要的数字。当然,这会导致每个项目的开销增加,因此需要权衡取舍。

    3. 可扩展性 - chars 返回 Enumerable 的原因是因为它本身是作为枚举器实现的;如果您将一个块传递给它,该块将每个字符执行一次。这意味着不需要例如.chars.each do ... end;你可以做.chars do ... end。这使得对字符串的字符构造操作链变得容易。

    【讨论】:

    • 1.没有意义。它返回一个枚举器的事实与它返回一个数组的事实一样多是一个实现细节。关于2:枚举器懒惰的,不是可以的。请注意,尽管有问题标题,但您可以从代码示例中看到返回类型是 Enumerator,而不是 Enumerable(这是不可能的)。
    • @sepp2k:在您发表评论之前已经将动词固定在 2 中;现在我也修正了错字。谢谢。我对 1 的观点很简单,有很多东西可以实现 Enumerable。没有多少类会费尽心思将自己打造成像数组一样的东西。从这个意义上说,枚举器更通用,因此更“抽象”。
    • 是的,有很多东西实现了 Enumerable。那么一个实现了 Enumerable(Enumerator)的类如何比使用另一个实现 Enumerable(Array)的类更通用呢?
    • @sepp2k:只要您将结果视为可枚举的,都可以。但是,如果您假设您正在获取一个数组并将其视为一个数组,那么它就不再是非常通用的了。由于 OP 正在查询结果的类,我想我会把这个区别扔进去。
    • 这与假设您得到一个 Enumerator 并将其视为 Enumerator 有何不同?
    【解决方案5】:

    '大多数人认为字符串是一个字符数组'......只有当你像 C 或其他语言一样思考时。恕我直言,Ruby 的面向对象比这要先进得多。大多数Array 操作往往更像Enumerable,所以这样可能更有意义。

    数组非常适合随机访问不同的索引,但字符串很少被特定索引访问。 (如果您尝试访问特定索引,我怀疑您可能正在做功课)

    如果您尝试检查每个字符,则 Enumerable 有效。使用 Enumberable,您可以访问 mapeachinject 等。对于替换,还有字符串函数和正则表达式。

    坦率地说,我想不出现实世界需要一系列字符。

    【讨论】:

    • 我不太认同这个解释。返回一个数组更符合“最小惊讶原则”,而不是“引用”一个在你有机会使用它之前可能会改变的对象。但这只是我的看法。
    • 一定是,我从来没有觉得它令人惊讶。你已经说明了这个问题。您假设一个字符串是一个字符数组。在 C 和其他一些类似 C 的语言中确实如此,但在 Ruby 中这是错误的假设。
    【解决方案6】:

    这完全符合1.9的精神:尽可能返回枚举数。 String#bytes、String#lines、String#codepoints,还有像 Array#permutation 这样的方法都返回一个枚举器。

    在 ruby​​ 1.8 String#to_a 中产生了一个行数组,但该方法在 1.9 中消失了。

    【讨论】:

    • 好的,但是为什么像 split 和 scan 这样的方法仍然返回数组呢?
    • 否则向后兼容性会被严重破坏。
    • 这是正确的答案,但我认为这是一个糟糕的决定。 String#chars 应该返回一个数组。
    • 看起来 #chars 和 #lines 在 Ruby 2.0 中已更改为返回数组而不是枚举器。 (如果需要枚举器,可以使用 each_char 和 each_line。)请参阅 ruby-doc.org/core-2.0/String.html#method-i-charsruby-doc.org/core-2.0/String.html#method-i-lines
    猜你喜欢
    • 2014-01-28
    • 1970-01-01
    • 1970-01-01
    • 2016-11-22
    • 2012-01-26
    • 1970-01-01
    • 2019-11-13
    • 2012-07-08
    • 1970-01-01
    相关资源
    最近更新 更多