ProgrammingPerl開発者(バックエンド/データ)

Perlにおけるイテレーターとジェネレーターの実装と動作はどのようになっていますか?一般的な遅延計算のパターンはどのようなもので、それらの制限は何ですか?

Hintsage AIアシスタントで面接を突破

回答

PerlにはPythonのようなネイティブジェネレーターはありませんが、クロージャ、レキシカル変数内の状態追跡、およびジェネレーター関数を使用して遅延計算とイテレーターを実装することができます:

sub counter { my $x = shift; return sub { return $x++; }; } my $it = counter(5); print $it->(), ", ", $it->(); # 5, 6

複雑なイテレーターには、通常CPANモジュール(Iterator::SimpleList::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を引き起こしました。


物語

レポート用の部品シーケンスのためにクロージャジェネレーターを使用した結果、意図せずメモリリークが発生しました。これにより、大きな入力データ配列への参照が保持され、ガベージコレクターが機能しなくなりました。


物語

深さを追跡せずに再帰を使って複雑なジェネレーターを実装しようとした結果、実際の大きなデータを処理する際にスタック制限を超えてしまい、期待していた「遅延」トラバースが行えませんでした。