为什么Enumerable在Ruby中没有length属性?

Why does Enumerable not have a length attribute in Ruby?

至少在Ruby 1.9.3 中,Enumerable 对象没有length 属性。这是为什么?

不保证可枚举的长度 - 混合可枚举的对象的唯一要求是它响应 #each,这导致它 return 系列中的下一个项目, 和 #<=> 允许比较可枚举提供的值。 #sort 之类的方法将在排序过程中枚举整个集合,但可能无法提前知道集合的边界。考虑:

class RandomSizeEnumerable
  include Enumerable
  def each
    value = rand 1000
    while value != 500
      yield value
      value = rand 1000
    end
  end

  # Not needed for this example, but included as a part of the Enumerable "interface".
  # You only need this method if #max, #min, or #sort are used on this class.
  def <=>(a, b)
    a <=> b
  end
end

此可枚举将被调用,直到迭代器生成值“500”,这将导致它停止枚举。收集并排序结果集。但是,#length 方法在此上下文中没有意义,因为在迭代器耗尽之前,长度是不可知的!

我们可以对 #sort 这样的结果调用 #length,因为它们 return 是一个数组,但是:

p RandomSizeEnumerable.new.sort.length # 321
p RandomSizeEnumerable.new.sort.length # 227
p RandomSizeEnumerable.new.sort.length # 299

通常,#length用于长度已知且可以在常数时间内returned,而#count(有时#size)往往是当可能无法提前知道长度并且需要通过迭代结果集(因此,需要线性时间)来计算长度时使用。如果您需要 Enumerable 提供的结果集的大小,请尝试使用 .to_a.length #count.

Enumerable 并不是真正的 class,它是一个模块 - 多个 classes 使用的横切功能的集合。

例如,ArraySetHash 都是 include 它 - 您可以对它们调用任何 Enumerable 方法。

Enumerable 值得注意的是它需要很少的 "host" class。您需要做的就是定义 each 方法和 include Enumerable,您可以免费获得所有这些方法!示例:

class CountUntil
  def initialize(number)
    @number = number
  end

  include Enumerable

  def each
    current = 0
    while current < @number
      yield current
      current += 1
    end
  end
end

# Usage:

CountUntil.new(10).map { |n| n * 5 }
# => [0, 5, 10, 15, 20, 25, 30, 35, 40, 45]

如您所见,我从未定义 CountUntil#map,但我通过包含 Enumerable.

免费获得了它

解决您关于 length 的问题:并非所有包含 Enumerable 的 class 都定义了长度,尽管大多数都定义了长度。例如,Enumerator 可用于创建无限流。

Enumerable有count方法,通常是枚举的直观"length"。

但为什么不叫它"length"呢?好吧,因为它的运作方式非常不同。在 Ruby 的内置数据结构中,如 ArrayHashlength 只是检索数据结构的预先计算的大小。它应该总是立即 return。

然而,对于 Enumerable#count,它无法知道它在哪种结构上运行,因此没有快速、巧妙的方法来获取枚举的大小(这是因为 Enumerable 是一个模块,可以包含在任何 class) 中。它获得枚举大小的唯一方法是实际枚举它并在进行时进行计数。对于无限枚举,count 将(适当地)永远循环并且永远不会 return.