W Perl nie ma natywnych generatorów, jak w Pythonie, ale można zaimplementować leniwe obliczenia i iteratory za pomocą zamknięć, śledzenia stanu w zmiennych leksykalnych oraz funkcji-generatorów:
sub counter { my $x = shift; return sub { return $x++; }; } my $it = counter(5); print $it->(), ", ", $it->(); # 5, 6
Dla złożonych iteratorów często używa się modułów CPAN (Iterator::Simple, List::Gen). Klasyczny leniwy wzorzec — zwracanie anonimnej podprogramy z zapisanym stanem.
Minusy: brak wbudowanego wsparcia dla yield, wielu modułom CPAN brakuje kompozycyjności. Rekurencja również ogranicza się do rozmiaru stosu.
Czy można zaimplementować nieskończoną leniwą listę liczb Fibonacciego w Perl bez przepełnienia pamięci?
Odpowiedź: Tak, za pomocą zamknięcia:
sub fibonacci { my ($a, $b) = (0, 1); return sub { ($a, $b) = ($b, $a+$b); return $a; }; } my $fib = fibonacci(); print $fib->(), ", ", $fib->(), ", ", $fib->();
Ale jeśli umieścisz wyniki w tablicy, z czasem przepełni pamięć (tzn. naprawdę „leniwy” jest tylko sam generator).
Historia
Na projekcie zaimplementowano własny iterator do przeszukiwania ogromnego pliku, zaimplementowany przez tablicę wewnątrz obiektu. Iterator ładował cały plik do pamięci — i przy wzroście pliku serwis zaczął wywoływać OOM przy pracy z wieloma instancjami.
Historia
Zamknięcie-generator dla sekwencji elementów do raportu prowadziło do nieoczekiwanej przecieku pamięci — wewnątrz zamknięcia przypadkowo utrzymywana była referencja do dużej tablicy danych wejściowych, co uniemożliwiało odśmiecanie przez zbieracza.
Historia
Próba zaimplementowania złożonego generatora za pomocą rekurencji bez śledzenia głębokości doprowadziła do przekroczenia limitu stosu przy przetwarzaniu naprawdę dużych danych, zamiast oczekiwanego „leniwego” przeszukiwania.