9

他の誰かのAPIをデフォルトの変数$_と組み合わせて使用​​しているときに、私は捕まりました。

foreach (@rps_server_details) {
    @server_data = ();
    @server_data = split(/,/);
    @$esp_hosts = ();
    $filters{server_name} = $server_data[0];
    print "--->$_<--\n";
    $esp_hosts = $esp->get_hosts(fields => $fields, %filters) || die "$@";
    print "--->$_<--\n";

このための出力は次のとおりです。

--->igrid8873.someone.com,app_10<--
Use of uninitialized value in concatenation (.) or string at ./rps_inv_lookup.pl line 120.
---><--

$ _に依存する代わりに、独自のループ変数を指定すると、問題が修正されます。

他の誰かが書いたAPIと組み合わせて$_を使用することで、私は単純なだけですか?それとも、これはそのAPIモジュールのバグですか?

4

3 に答える 3

9

これはAPIのバグです。関数で使用する場合$_は、を追加することが重要です

local($_);

関数内で呼び出し元のを壊さないようにするか、ライブラリ関数で他の人が呼び出す関数を$_使用しないようにします。$_

Perlバージョン>5.9.1に制限できる場合は、$ _字句を作成することもできます。これにより、よりも理解しやすくなりますlocal

my $_;

しかし、これは以前のバージョンのPerlでは壊れます。

man perlvarから:

グローバル変数と同様$_に、これは場合によっては望ましくない副作用につながる可能性があります。$_perl 5.9.1以降、ファイルまたは「my」を含むブロックで宣言することにより、の字句バージョンを使用できるようになりました。さらに、「」を宣言する と、現在のスコープでour $_グローバルが復元されます。$_

于 2010-10-29T18:09:53.133 に答える
6

私はそれが次のように言うでしょう:

  1. あなたの側のベストプラクティスの違反(常に-ローカル-可能な限り可変スコープを使用$_し、発生した問題だけのために使用を避けてください)

  2. ベストプラクティスの同じ違反によって引き起こされたAPIのバグと相まって、perldocperlvarによって禁止されているように特別な変数をローカライズしていませんlocal $_

perldocに加えて、APIはPerlのベストプラクティスに違反しています(Conwayの本のルールのように):

セクション5.6。句読点変数のローカライズ

句読点変数を変更せざるを得ない場合は、ローカライズしてください。

「ローカリゼーション」で前述した問題は、句読点変数の値を変更しなければならない場合はいつでも発生する可能性があります(多くの場合、I / O操作で)。すべての句読点変数はスコープがグローバルです。これらは、完全に何になるかを明示的に制御します。他のほとんどの言語での暗黙の動作:出力バッファリング、入力行番号、入力および出力行の終わり、配列の索引付けなど。

通常、句読点変数を最初にローカライズせずに変更することは重大なエラーです。ローカライズされていない割り当ては、自分で作成しておらず、単に使用しているモジュールであっても、システムのまったく関係のない部分のコードの動作を変更する可能性があります。

ローカルを使用することは、グローバル変数の値を一時的に変更するための最もクリーンで堅牢な方法です。変数が制御する可能性のある「周囲の動作」の影響を最小限に抑えるために、常に可能な限り最小のスコープで適用する必要があります。

perldoc perlvarの完全なドキュメントもここにあります-Webページで「nasty_break」という単語を検索します(ページ内の直接リンクは見つかりませんでしたが、ページの先頭に近いです)

このドキュメントで説明されているほとんどの特殊変数のデフォルト値を変更するときは、十分に注意する必要があります。ほとんどの場合、これらの変数を変更する前にローカライズする必要があります。ローカライズしないと、変更した特別な変数のデフォルト値に依存する他のモジュールに変更が影響する可能性があるためです。これは、ファイル全体を一度に読み取る正しい方法の1つです。

  1. $ fh、 "<"、 "foo"を開くか、$!;を死ぬ
  2. ローカル$/; #ローカライズされたスラップモードを有効にする
  3. 私の$content=;
  4. $fhを閉じます。

しかし、次のコードはかなり悪いです:

  1. $ fh、 "<"、 "foo"を開くか、$!;を死ぬ
  2. undef $ /; #スラップモードを有効にする
  3. 私の$content=;
  4. $fhを閉じます。

他のモジュールは、デフォルトの「ラインモード」でファイルからデータを読み取りたい場合があるため、今提示したコードが実行された場合、同じ内部で実行されている他のコードのグローバル値$/が変更されます。 Perlインタプリタ。

通常、変数がローカライズされている場合、この変更が可能な限り最短のスコープに影響を与えることを確認する必要があります。したがって、すでに短い{}ブロック内にいない限り、自分で作成する必要があります。例えば:

  1. 私の$content='';
  2. $ fh、 "<"、 "foo"を開くか、$!;を死ぬ
  3. {{
  4. ローカル$/;
  5. $ content =;
  6. }
  7. $fhを閉じます。

独自のコードが壊れてしまう例を次に示します。

  1. for(1..5){
  2. nasty_break();
  3. 印刷"$_";
  4. }
  5. sub nasty_break {
  6. $ _ = 5;
  7. #$_で何かをする
  8. }

あなたはおそらくこのコードが印刷されることを期待しています:

  1. 1 2 3 4 5

しかし、代わりに次のようになります。

  1. 5 5 5 5 5

なんで?nasty_break()は、最初にローカライズせずに$_を変更するためです。修正はlocal()を追加することです:

  1. ローカル$_= 5;
于 2010-10-29T18:08:14.647 に答える
2
foreach (@rps_server_details) {
    @server_data = ();
    @server_data = split(/,/);
    @$esp_hosts = ();
    $filters{server_name} = $server_data[0];
    print "--->$_<--\n";
    {
        local *_;  # disconnects the remaining scope from the implicit 
                   # variables so you can clean up after the dirty api.
                   # NOTE: Submit a bug report against the offending module.
                   #       If you notice this across multiple api features
                   #       consider finding a different module for this task.
        $esp_hosts = $esp->get_hosts(fields => $fields, %filters) || die "$@";
    }
    print "--->$_<--\n";
于 2010-10-30T09:28:04.563 に答える