为什么Enumerator包含Enumerable

时间:2013-04-10 23:21:43

标签: ruby lazy-evaluation enumerable

加上这个,这是一个很酷的Enumerator(懒惰序列)从1到(Ruby可以表示的最大Float):

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

进入lala land。而且我的意思是它不会在不到5秒的时间内返回。

哦,这是一个线索:

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

似乎枚举器(e)将#drop({1}}方法从Enumerable(模块)混合到Enumerator(类)中。现在为什么Ruby会把Enumerable混合到你要问的Enumerator中?我不知道。但在Enumerator in Ruby 1.9.3Enumerator in Ruby 2.0中都记录了它。

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

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

PS注意到Ruby 2.0定义了Enumerator::LazyEnumerator的子类),它将一堆Enumerable方法定义为总是懒惰的。这里闻起来有点腥味。为什么在非惰性和某些情况下破坏方法(进入Enumerator)只是转而在子类(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>

2 个答案:

答案 0 :(得分:3)

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

Ruby的集合操作不是类型保留的。他们总是返回Array,无论他们调用什么类型的集合。这也是.NET所做的,除了类型总是IEnumerable之外,它更有用(因为更多的东西可以表示为IEnumerable而不是Array ,例如无限序列),同时不太有用(因为IEnumerable的界面远小于Array的界面,所以你可以对它做的操作较少)。

这允许Ruby的集合操作一次实现,而不会重复。

这也意味着将您自己的集合集成到Ruby的集合框架中非常容易:只需实现each,mixin Enumerable即可。如果Ruby的未来版本添加了一个新的集合方法(例如,在Ruby 1.9中为flat_map),则不必执行任何,它也适用于您的集合。

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

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

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

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

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

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

答案 1 :(得分:1)

回应第一部分:

  

“进入lala land。而且我的意思是它不会减少回归   超过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,所以它的行为就是这样。

来源:Enumerable