В C # вы можете сделать что-то вроде этого:
public IEnumerable<T> GetItems<T>()
{
for (int i=0; i<10000000; i++) {
yield return i;
}
}
Возвращает перечисляемую последовательность из 10 миллионов целых чисел без выделения коллекции в памяти такой длины.
Есть ли способ сделать эквивалентную вещь в Ruby? Конкретный пример, с которым я пытаюсь разобраться, - это сглаживание прямоугольного массива в последовательность значений для перечисления. Возвращаемое значение не обязательно должно быть Array
или Set
, а скорее какой-то последовательностью, которую можно повторять / перечислять только по порядку, а не по индексу. Следовательно, вся последовательность не должна быть распределена в памяти одновременно. В .NET это IEnumerable
и IEnumerable<T>
.
Любое разъяснение терминологии, используемой здесь, в мире Ruby, было бы полезно, так как я более знаком с терминологией .NET.
EDIT
Возможно, мой первоначальный вопрос не был достаточно ясен - я думаю, что тот факт, что yield
имеет очень разные значения в C #, а Ruby является причиной путаницы здесь.
Мне не нужно решение, которое требует, чтобы мой метод использовал блок. Я хочу решение, которое имеет фактическое возвращаемое значение. Возвращаемое значение обеспечивает удобную обработку последовательности (фильтрация, проекция, объединение, архивирование и т. Д.).
Вот простой пример того, как я могу использовать get_items
:
things = obj.get_items.select { |i| !i.thing.nil? }.map { |i| i.thing }
В C # любой метод, возвращающий IEnumerable
, который использует yield return
, заставляет компилятор генерировать конечный автомат за кулисами, который обслуживает это поведение. Я подозреваю, что чего-то подобного можно достичь, используя продолжения Ruby, но я не видел примера и не совсем ясно, как это можно сделать.
Действительно кажется возможным, что я мог бы использовать Enumerable
для достижения этой цели. Простым решением для нас будет Array
(который включает в себя модуль Enumerable
), но я не хочу создавать промежуточную коллекцию с N элементами в памяти, когда можно просто предоставить их лениво и вообще избежать скачка памяти .
Если это все еще не имеет смысла, рассмотрите приведенный выше пример кода. get_items
возвращает перечисление, для которого вызывается select
. То, что передается в select
, является экземпляром, который знает, как предоставить следующий элемент в последовательности, когда это необходимо. Важно отметить, что вся коллекция предметов еще не рассчитана. Только когда select
понадобится предмет, он попросит его, и скрытый код в get_items
начнет действовать и предоставит его. Эта лень несет по цепочке, так что select
вытягивает следующий элемент из последовательности, только когда map
запрашивает его. Таким образом, длинная цепочка операций может выполняться одновременно для одного элемента данных. Фактически, код, структурированный таким образом, может даже обрабатывать бесконечную последовательность значений без каких-либо ошибок памяти.
Итак, этот тип лени легко кодируется в C #, и я не знаю, как это сделать в Ruby.
Надеюсь, это будет понятнее (я постараюсь не писать вопросы в 3 часа ночи в будущем.)