プログラミング Perl がファイルハンドルにローカル (自分ではない) を使用するのはなぜですか?
-
03-07-2019 - |
質問
読み進めてみると Perl のプログラミング, 、第 2 版、51 ページ、何か混乱しています:
sub newopen {
my $path = shift;
local *FH; #not my!
open (FH, $path) || return undef;
return *FH;
}
$fh = newopen('/etc/passwd');
私のことはわかっていますが、なぜ私の使用が推奨されないのですか?今のところ、my() を使用しても問題が発生することはわかりません。
ありがとう!
解決
控えめな答えは、 my * FH
は構文エラーであるため、 local
を使用する必要があるということです。
"右" (しかし、あまり啓発的ではありません)答えは、あなたが間違っているということです。代わりに、レキシカルファイルハンドルと3つの引数形式の open
を使用する必要があります。
sub newopen {
my $path = shift;
my $fh;
open($fh, '<', $path) or do {
warn "Can't read file '$path' [$!]\n";
return;
}
return $fh;
}
実際に答えるには、なぜには、字句変数とグローバル変数の違い、および変数のスコープとその継続時間の違いの説明が必要です。
変数のスコープは、その名前が有効なプログラムの部分です。スコープは静的プロパティです。一方、変数の継続時間は動的プロパティです。期間は、プログラムの実行中に変数が存在し、値を保持している時間です。
my
はレキシカル変数を宣言します。字句変数のスコープは、宣言のポイントから囲んでいるブロック(またはファイル)の終わりまでです。競合することなく、異なるスコープに同じ名前の他の変数を含めることができます。 (重複するスコープで名前を再利用することもできますが、そうしないでください。)字句変数の期間は、参照カウントによって管理されます。名前が特定のスコープ内で有効でない場合でも、変数への参照が少なくとも1つある限り、値は存在します! my
にはランタイム効果もあります。指定された名前の new 変数を割り当てます。
local
は少し異なります。グローバル変数で動作します。グローバル変数には、グローバルスコープ(名前はどこでも有効)とプログラムの全ライフ期間があります。 local
は、グローバル変数の value を一時的に変更します。これは「動的スコープ」と呼ばれることもあります。変更は local
宣言のポイントから始まり、囲んでいるブロックの終わりまで持続し、その後、古い値が復元されます。新しい値はブロックに限定されないことに注意することが重要です-それはどこでも見ることができます(呼び出されたサブルーチンを含む)。参照カウントルールは引き続き適用されるため、変更が期限切れになった後、ローカライズされた値への参照を取得して保持できます。
例に戻る: * FH
はグローバル変数です。より正確には、「typeglob」です。 -一連のグローバル変数のコンテナ。 typeglobには、各基本変数タイプ(スカラー、配列、ハッシュ)に加えて、その他のいくつかのスロットが含まれています。歴史的に、Perlはファイルハンドルを保存するためにtypeglobを使用し、それらを local
-izingすることで、お互いを破壊しないようにしました。字句変数にはtypeglobがないため、 my * FH
は構文エラーであると言っています。
Perlの最新バージョンでは、レキシカル変数を代わりにファイルハンドルとして使用できます。そして、それは「正しい」に私たちをもたらします。答えてください。
他のヒント
サンプルコードでは、組み込みサブルーチン open
の呼び出しでは、グローバル変数に相当する裸語をファイルハンドルとして使用しています。 Nathan Fellmanの答えが説明したように、 local
は、同じ名前の別のグローバル変数がスクリプトまたはモジュールの別の場所で定義されている場合、この裸の単語を現在のコードブロックにローカライズします。これにより、以前に定義されたグローバル変数が新しい宣言によって消去されるのを防ぎます。
これは昔のPerlの時代では非常に一般的な慣習でしたが、 Perl 5.6では、質問で示唆した my
宣言でスカラーを使用する方がはるかに良いです)ファイルハンドルを定義し、さらに、 open
への3つの引数呼び出しを使用します。
use Carp;
open my $error_log, '>>', 'error.log' or croak "Can't open error.log: $OS_ERROR";
余談ですが、標準の入出力の読み書きでは、2つの引数 open
を使用することをお勧めします:
use Carp;
open my $stdin, '<-' or croak "Can't open stdin: $OS_ERROR";
または、 IO :: File
クラスへのファイルハンドルを祝福するモジュール:
use IO::File;
my $error_log = IO::File->new('error.log', '>>') or croak "Can't open error.log: $OS_ERROR");
クレジットの大半はダミアンコンウェイ、優れた本Perl Best Practices の著者。 Perl開発を真剣に考えているなら、この本を購入するのはあなた自身の責任です。
my
がスタックに変数の新しいコピーを割り当て、ブロックを終了すると失われるためだと思います。 local
は、既存の * FH
を別の場所に保存し、既存の * FH
を上書きします。スタックを終了すると、古いものが復元されます。 my
を使用すると、ブロックを終了するときに * FH
typeglobがスコープ外になります。 local
を使用すると、既存のものが保持されるため、返品後も引き続き使用できます。
これは100%確かではありませんが、正しい方向に向けられる可能性があります。
ローカライズされたファイルハンドルこちらをご覧ください。それが説明されていると思います。