В Perl нет нативных генераторов, как в Python, но можно реализовать ленивые вычисления и итераторы с помощью замыканий, отслеживания состояния в лексических переменных и функций-генераторов:
sub counter { my $x = shift; return sub { return $x++; }; } my $it = counter(5); print $it->(), ", ", $it->(); # 5, 6
Для сложных итераторов часто используют CPAN-модули (Iterator::Simple, List::Gen). Классический ленивый паттерн — возврат анонимной подпрограммы с сохранённым состоянием.
Минусы: нет встроенной поддержки для yield, многим CPAN-модулям не хватает композитности. Рекурсия также ограничивается размерами стека.
Можно ли реализовать бесконечный ленивый список чисел Фибоначчи в Perl без переполнения памяти?
Ответ: Да, с помощью замыкания:
sub fibonacci { my ($a, $b) = (0, 1); return sub { ($a, $b) = ($b, $a+$b); return $a; }; } my $fib = fibonacci(); print $fib->(), ", ", $fib->(), ", ", $fib->();
Но если помещать результаты в массив, он со временем переполнит память (т.е. реально «ленивый» только сам генератор).
История
На проекте был написан собственный итератор для обхода огромного файла, реализованный через массив внутри объекта. Итератор загружал весь файл в память — и при росте файла сервис стал вызывать OOM при работе с несколькими экземплярами.
История
Замыкание-генератор для последовательности деталей к отчету приводило к неожиданной утечке памяти — внутри замыкания случайно удерживалась ссылка на большой массив входных данных, что не позволяло сборщику мусора отработать.
История
Попытка реализовать сложный генератор с помощью рекурсии без отслеживания глубины привела к превышению лимита стека при обработке реально больших данных, вместо ожидаемого «ленивого» обхода.