为什么Enumerable在Ruby中没有length属性?
至少在Ruby 1.9.3中, Enumerable
对象没有length
属性。
据我所知,任何Enumerable
都是一个集合,如sort
和find_index
等方法所certificate。
一个集总是有一个定义明确的长度(……对吗?),为什么这不是一个属性?
Enumerable有count
方法,它通常是枚举的直观“长度”。
但为什么不把它称为“长度”? 好吧,因为它的运作方式非常不同。 在Ruby的内置数据结构(如Array
和Hash
, length
只检索数据结构的预先计算大小。 它应该总是立即返回。
但是,对于Enumerable#count
,它无法知道它正在运行什么类型的结构,因此没有快速,巧妙的方法来获取枚举的大小(这是因为Enumerable
是一个模块,并且可以包含在任何模块中类)。 获取枚举大小的唯一方法是实际枚举它并按其计算。 对于无限枚举, count
将(适当地)永远循环并且永不返回。
枚举不能保证有长度 – 对于Enumerable混合的对象的唯一要求是它响应#each
,导致它返回系列中的下一个项目, #<=>
允许比较提供的值由可数。 像#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
方法没有意义,因为在迭代器耗尽之前,长度是不可知的!
我们可以在#length
之类的结果上调用#length
,因为它们返回一个数组,但是:
p RandomSizeEnumerable.new.sort.length # 321 p RandomSizeEnumerable.new.sort.length # 227 p RandomSizeEnumerable.new.sort.length # 299
通常,当长度已知并且可以在恒定时间内返回时使用#count
,而当提前知道长度并且需要通过迭代计算时,倾向于使用#count
(有时是#size
)。结果集(因此,采用线性时间)。 如果您需要Enumerable提供的结果集的大小,请尝试使用.to_a.length
#count
。
Enumerable
并不是真正的类,它是一个模块 – 由多个类使用的交叉function的集合。
例如, Array
, Set
和Hash
都include
它 – 您可以调用它们上的任何Enumerable
方法。
Enumerable
值得注意的是它只需要很少的“主机”类。 您需要做的就是定义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
类都定义了长度,即使大多数类都有。 例如, Enumerator
可用于创建无限流。