Programmingバックエンド開発者

Perlにおけるレキシカル変数とパッケージ変数の動作:my、our、stateの違いは何か、適切な使用法と実際に遭遇するエラーは何か?

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

回答

Perlには、変数宣言の3つの主要なタイプがあります:myourstate

  • myは、コードブロック内のスコープを持つレキシカル変数を作成します(レキシカルスコープ)。
  • ourは、パッケージ内でグローバルにアクセス可能なパッケージ変数を作成しますが、レキシカルブロック内でのみ見えます。他のモジュール間で名前空間を整理するために便利です。
  • stateはPerl 5.10で追加され、サブルーチンの呼び出し間で値を保持するレキシカル変数を作成します(実質的には静的変数です)。
sub example_state { state $counter = 0; $counter++; return $counter; } for (1..3) { print example_state(), "\n"; # 1、2、3が表示されます }

正しい使用法:

  • my — グローバルアクセスの理由がない限り、変数にはほぼ常に好ましい選択です。
  • our — モジュール間で変数をエクスポートする必要がある場合に使用します。
  • state — 変数が関数の呼び出し間で「記憶」する必要がある場合(例えば、呼び出しのカウンタ)に使用します。

ひっかけの質問

ourで宣言された変数は、myと同様にクロージャー内でキャプチャされることができますか?

通常は「はい、便利です」と答えますが、それは正しくありません。our変数はパッケージに対してグローバルであり、クロージャーは実際には作成時にその値をバインドするのではなく、グローバルな名前を介してアクセスしています。したがって、クロージャーの外部でその「値」が変更されると、すべてのクロージャーに影響を与えます!

our $x = 10; my $closure = sub { return $x; }; $x = 42; print $closure->(); # 10ではなく42を返します

このトピックの微妙な点を知らないための実際のエラーの例


物語

大規模なログ解析スクリプトでは、異なる関数から「便利にアクセスする」ためにourを使用して変数が宣言されていました。その結果、ある関数でこれらの変数が変更されると、別の関数で予期しない動作が発生し、ログの並行処理が壊れました。


物語

関数の呼び出し間で値を保持する必要がある変数(例えば、再帰的な巡回内のカウンタ)にmyを使用することで、各呼び出し時に値がリセットされ、不正な巡回ロジックが発生しました。これをstateに置き換えることで状況が改善されました。


物語

インポートされるモジュール内でのourの誤用(Exporterを介さずに変数をエクスポートすること)は、同じ名前の変数を使用する2つの異なるモジュールを接続する際に名前の衝突を引き起こしました。その結果、データがあるコンテキストから別のコンテキストに「飛び移る」ことになりました。