我在合理的时间内获得Enumerator
实例的无限序列时遇到了麻烦。我首先尝试了drop
和take
链,但是当drop
尝试返回无限Array
时,它会永远挂起。接下来,我改变了这些方法的顺序,但是我仍需要等待大约十分钟才能在十亿分之一样本后获得100个值:
print exbioseq.drop(10**7).take(100)
可以做任何事情来更快地获得切片吗?
答案 0 :(得分:2)
Enumerator
是非常通用的界面,它只对它正在遍历的“集合”做出非常简单的假设。特别是,它实际上只支持两个操作:获取当前元素并迭代到下一个元素。
鉴于这两项操作,如果你想获得第1000万个元素,你只能做一件事:迭代1000万次。这需要时间。
没有“切片”Enumerator
这样的东西。一个Enumerator
枚举。就是这样。
现在,正如您所发现的,还有另一个问题:Ruby的集合操作不是类型保留的。无论您拨打map
或select
或take
或其他任何类型的集合,它都会返回相同的类型:完全实现的,具体的,严格的Array
。这就是大多数语言中大多数集合框架的工作方式,例如:在.NET中,所有集合操作都返回IEnumerable
。这是因为大多数这些方法在Enumerable
mixin中只有一个共同的实现。
Smalltalk是一个例外,但还有另一个问题:每个集合类型都会重复收集操作。每种集合类型都有自己几乎不同的collect:
,select:
等实际复制和粘贴实现。此代码重复很难维护,并且对任何想要集成其的人造成很大负担拥有集合到框架中。在Ruby中,很容易:实现each
,mixin Enumerable
并且你已经完成了。
注意:从Ruby 1.9开始,实际上存在一些的重复:Hash
实现了自己的select
版本,它实际上返回了Hash
而不是Array
。所以,现在,不仅代码重复而且界面中存在不对称:select
的所有实现都返回Array
,除了Hash
中的那个。
Scala 2.8集合框架是有史以来第一次想出如何在没有代码重复的情况下提供类型保留集合操作。但Ruby的集合框架是在Scala 2.8之前15年设计的,所以它无法利用这些知识。
在Ruby 2.0中,存在惰性Enumerator
,其中所有集合操作都返回另一个惰性Enumerator
。但是这对你没有帮助:唯一的区别是懒惰的Enumerator
会延迟1000万次迭代,直到你真正print
这些值。它仍然必须执行那1000万次迭代,因为根本没有其他办法。
如果要切片,则需要可切片的数据结构,例如Array
。