2

挖这个,这是一个很酷的Enumerator(惰性序列)从 1 到(最大的FloatRuby 可以表示):

1.9.3-p327 :014 > e = (1..Float::INFINITY).each

看看我们如何抓住序列的前面:

1.9.3-p327 :015 > e.first
 => 1 
1.9.3-p327 :016 > e.take(2)
 => [1, 2] 

这是好东西吧?我也这么认为。但后来这个:

1.9.3-p327 :017 > e.drop(2).first

进入拉拉地。我的意思是它不会在不到 5 秒内返回。

哦,这里有一个线索:

1.9.3-p327 :020 > p e.method(:drop)
#<Method: Enumerator(Enumerable)#drop>

似乎 Enumerator ( e) 将其#drop方法从Enumerable(module) 混合到Enumerator(class) 中。现在你问为什么 Ruby 会混Enumerable进去Enumerator?我不知道。但是它Enumerator在 Ruby 1.9.3EnumeratorRuby 2.0中都有记录。

我看到的问题是在Enumerable工作中定义的某些方法或工作类型Enumerator。示例包括#first#take。至少还有一个:#drop不起作用。

在我看来,Enumerator包括Enumerable是一个错误。你怎么看?

PS 注意到 Ruby 2.0 定义了Enumerator::Lazy(subclass of Enumerator),它定义了一堆Enumerable总是惰性的方法。这里有一股腥味。为什么混合非惰性和在某些情况下损坏的方法 (into Enumerator) 只是为了在子类 (of ) 中转身并提供惰性替代方案Enumerator

也可以看看:

1.9.3-p327 :018 > p e.method(:first)
#<Method: Enumerator(Enumerable)#first>
1.9.3-p327 :020 > p e.method(:drop)
#<Method: Enumerator(Enumerable)#drop>
4

2 回答 2

3

这也是许多其他集合框架所共有的设计选择。

Ruby 的集合操作不是类型保留的。无论调用什么类型的集合,它们总是返回一个。Array这也是,例如,.NET 所做的,除了类型 always IEnumerable,这既更有用(因为更多的东西可以表示为 anIEnumerable而不是Array,例如无限序列),但同时也不太有用(因为的接口IEnumerable比 的要小很多Array,所以可以对它进行的操作较少)。

这使得 Ruby 的收集操作可以实现一次,无需重复。

这也意味着将您自己的集合集成到 Ruby 的集合框架中非常容易:只需实现each, mixinEnumerable即可。如果 Ruby 的未来版本添加了新的收集方法(例如flat_map,在 Ruby 1.9 中),您不必做任何事情,它也可以与您的集合一起使用。

另一种设计选择是使所有收集操作保持类型。因此,所有集合操作都返回它们被调用的类型。

有一些语言可以做到这一点。然而,它是通过将所有集合方法复制并粘贴到所有集合类中来实现的,即大量代码重复。

这意味着如果你想将你自己的集合添加到集合框架中,你必须实现集合协议的每一个方法。如果该语言的未来版本添加了新方法,那么您必须发布您的集合的新版本。

Scala 2.8 的集合框架是第一次有人想出如何在不重复代码的情况下进行类型保留的集合操作。但那是在设计 Ruby 的集合框架之后很久。在设计 Ruby 的集合框架时,根本不知道如何在不重复代码的情况下进行类型保留的集合操作,Ruby 的设计者选择反对重复。

从 Ruby 1.9 开始,实际上存在一些重复。一些Hash方法被复制以返回Hashes 而不是Arrays。您已经提到了 Ruby 2.0 Enumerator::Lazy,它重复了许多Enumerable返回方法Enumerator::Lazy

可以使用 Scala 在 Ruby 中使用的相同技巧,但它需要对集合框架进行彻底改造,这将使每个现有的集合实现都过时。Scala 能够做到这一点是因为当时几乎没有任何用户群。

于 2013-04-11T00:38:54.127 回答
1

针对第一部分:

“进入啦啦之地。我的意思是它不会在不到 5 秒内返回。”

这种行为似乎与这些方法应该做的一致:

take(n) → array # Returns first n elements from enum.

这意味着您只需要迭代到 N 即可返回它。

drop(n) → array # #Drops first n elements from enum, and returns rest elements in an array.

这意味着它需要其余元素才能返回它们。并且由于您的上限是Float::INFINITY它的行为。

来源:可枚举

于 2013-04-10T23:48:30.623 回答