Получите часть Enumerator эффективно
У меня проблемы с получением кусочка бесконечной последовательности Enumerator
экземпляр в разумный срок. Я впервые попробовал drop
а также take
цепь, но она навсегда висела как drop
пытался вернуть бесконечное Array
, Затем я изменил порядок этих методов, но мне все еще приходится ждать около десяти минут, чтобы получить 100 значений после десятимиллионной выборки:
print exbioseq.drop(10**7).take(100)
Можно ли что-нибудь сделать, чтобы получить кусочек быстрее?
1 ответ
Enumerator
Это очень общий интерфейс, он делает только очень простые предположения о "коллекции", которую он пересекает. В частности, он действительно поддерживает только две операции: получить текущий элемент и перейти к следующему элементу.
Учитывая эти две операции, если вы хотите получить 10-миллионный элемент, вы можете сделать только одну вещь: выполнить итерацию 10 миллионов раз. Что требует времени.
Нет такой вещи как "нарезка" Enumerator
, Enumerator
перебирает. Вот и все.
Теперь, как вы обнаружили, есть еще одна проблема: операции сбора в Ruby не сохраняют тип. Неважно, какой тип коллекции вы называете map
или же select
или же take
или что-то еще, он всегда будет возвращать один и тот же тип: полностью реализованный, конкретный, строгий Array
, Вот как работает большинство фреймворков для коллекций в большинстве языков, например, в.NET все операции по сбору возвращают IEnumerable
, Это потому, что большинство из этих методов имеют только одну общую реализацию в Enumerable
Mixin.
Smalltalk - исключение, но есть и другая проблема: операции сбора дублируются для каждого типа сбора. Каждый тип коллекции имеет свою собственную практически неопределенную реализацию копирования и вставки collect:
, select:
и т. д. Такое дублирование кода сложно поддерживать, и оно ложится тяжелым бременем на каждого, кто хочет интегрировать свою собственную коллекцию в платформу. В Ruby это просто: реализовать each
, миксин Enumerable
и вы сделали.
Примечание. Начиная с Ruby 1.9, на самом деле есть некоторое дублирование: Hash
реализует свою собственную версию select
который на самом деле возвращает Hash
и не Array
, Итак, теперь не только дублирование кода, но и асимметрия в интерфейсе: все реализации select
вернуть Array
с за исключением одного в Hash
,
Платформа сбора Scala 2.8 - это первый случай, когда кто-то понял, как обеспечить операции сбора типов с сохранением типов без дублирования кода. Но среда разработки Ruby была разработана за 15 лет до Scala 2.8, поэтому она не может воспользоваться этими знаниями.
В Ruby 2.0 есть ленивые Enumerator
s, где все операции сбора возвращают еще один ленивый Enumerator
, Но это вам не поможет: единственная разница в том, что ленивый Enumerator
задержит 10 миллионов итераций, пока вы на самом деле print
ценности. Он все еще должен выполнить эти 10 миллионов итераций, потому что просто нет другого способа сделать это.
Если вы хотите нарезку, вам нужна структура данных с возможностью среза, такая как Array
,