11

Programming Perl , 2nd Edition, Page 51を読んだとき、何かが私を混乱させました:

sub newopen {
    my $path = shift;
    local *FH;    #not my!
    open (FH, $path) || return undef;
    return *FH;
}

$fh = newopen('/etc/passwd');

私は知っていますが、なぜ私の使用を推奨しないのですか? これまでのところ、my() を使用した場合に問題が発生することはありません。

ありがとう!

4

5 に答える 5

27

陳腐な答えは、構文エラーであるlocalため、使用する必要があるということです。my *FH

「正しい」(しかしあまり啓発的ではない)答えは、あなたが間違っているということです。代わりにレキシカル ファイルハンドルと 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ランタイム効果もあります。指定された名前で新しい変数を割り当てます。

localは少し異なります。グローバル変数で動作します。グローバル変数には、グローバル スコープ (名前はどこでも有効) と、プログラムの存続期間全体があります。グローバル変数のlocal一時的に変更します。これは、「動的スコープ」と呼ばれることもあります。変更は宣言の時点から始まり、囲んでいるブロックの終わりまで持続し、その後古い値が復元されます。新しい値はブロックに限定されないことに注意することが重要です。これはどこでも表示されます (呼び出されたサブルーチンを含む)。参照カウント規則は引き続き適用されるため、変更の有効期限が切れた後も、ローカライズされた値への参照を取得して保持できます。local

例に戻る:*FHはグローバル変数です。より正確には、これは「typeglob」であり、一連のグローバル変数のコンテナーです。型グロブには、基本的な変数の型 (スカラー、配列、ハッシュ) のそれぞれに対応するスロットと、その他いくつかのものがあります。歴史的に、Perl はファイルハンドルを格納するために型グロブを使用し、localそれらを -izing することで、それらが互いに破壊しないようにしました。レキシカル変数には型グロブがないため、言ってmy *FHいるのは構文エラーです。

最近のバージョンの Perl レキシカル変数では、代わりにファイルハンドルとして使用できますし、使用する必要があります。そして、それは「正しい」答えに戻ります。

于 2009-03-05T20:42:32.800 に答える
20

サンプル コードでは、組み込みのサブルーチンへの呼び出しopenは、ファイル ハンドルとしてベア ワードを使用しています。これは、グローバル変数に相当します。Nathan Fellman の回答が説明したように、同じ名前の別のグローバル変数がスクリプトまたはモジュールの他の場所で定義されている場合、使用するlocalと、この裸の単語が現在のコード ブロックにローカライズされます。これにより、以前に定義されたグローバル変数が新しい宣言によって消去されるのを防ぐことができます。

これは古い Perl の時代には非常に一般的な方法でしたが、Perl 5.6 の時点では、ファイル ハンドルを定義するために (質問で示唆した宣言を使用して)スカラーを使用し、さらに 3 つの引数を使用する方がはるかに優れていmyます。に電話しopenます。

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、ファイル ハンドルをクラスに bless することもできます。

use IO::File;
my $error_log = IO::File->new('error.log', '>>') or croak "Can't open error.log: $OS_ERROR");

ここでの功績の大部分は、素晴らしい本の Perl Best Practices の著者である Damian Conway に帰されます。Perl の開発に真剣に取り組んでいるなら、この本を購入する義務があります。

于 2009-03-05T09:08:45.183 に答える
12

なぜあなたは時代遅れの本を読んでいるのですか。第3版は久しぶりです!どのバージョンのPerlを使用していますか?第2版​​では、Perl 5.004(5.4.x)またはその周辺について説明しています。

最近では、ファイルハンドルにtypeglob表記を使用しないでください。代わりに、「字句ファイルハンドル」(openを参照)またはFileHandleモジュール、あるいはその親戚の1つを使用してください。


ここに組み込まれたコメントを寄せてくれたMichaelSchwernとYsthに感謝します。

于 2009-03-05T08:02:50.350 に答える
0

myスタックに変数の新しいコピーを割り当て、ブロックを終了すると失われるためだと思います 。local既存の を別の場所に保存し、既存*FHの をオーバーライドします*FH。スタックを終了すると、古いものが復元されます。ブロックを終了するとmy*FH型グロブが範囲外になります。そのまま残っlocalているので、返却後も使い続けることができます。

これについて 100% 確信があるわけではありませんが、正しい方向に向けることができるかもしれません。

于 2009-03-05T08:47:35.530 に答える
0

Localized Filehandles hereを参照してください。それで説明できると思います。

于 2009-03-05T09:06:58.060 に答える