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を引き起こしました。
物語
レポート用の部品シーケンスのためにクロージャジェネレーターを使用した結果、意図せずメモリリークが発生しました。これにより、大きな入力データ配列への参照が保持され、ガベージコレクターが機能しなくなりました。
物語
深さを追跡せずに再帰を使って複雑なジェネレーターを実装しようとした結果、実際の大きなデータを処理する際にスタック制限を超えてしまい、期待していた「遅延」トラバースが行えませんでした。