サブルーチンの上書き

サブルーチンの上書きについてごめんなさい。 - ヒルズで働く@robarioの技ログ
も読んでください(><)


id:zetamattaさんから
「undef &main::symbol;」では駄目なんでしょうか?
とコメントを頂きました。全然ダメじゃないです。全然OKです。
恥ずかしながら「&main::symbol」でサブルーチンを呼び出した結果に「undef」が適用されるものだと勘違いしていました。

多分バグだと思うんですけどPerl::Critic v1.051 -severity 1はundef &main::symbolを(筆者と同様に)サブルーチン呼び出しだと勘違いして通りませんでした。
エントリが直しにくかったので、最後の結論部分だけ無理やり修正しました。
混乱させてしまって申し訳ないのですが、普通は「undef &main::symbol;」でOKです。

id:zetamattaさん、ありがとうございました。

Perlの(名前付き)サブルーチンを実行時に上書きするには、シンボルテーブルを書き換えます。

sub symbol {
    return 'CODE';
}

printf "symbol = %s\n", symbol();

*main::symbol = sub {
    return 'ANOTHER CODE';
};

printf "symbol = %s\n", symbol();

実行例

% perl -w symbol.pl
symbol = CODE
Subroutine main::symbol redefined at symbol.pl line 9.
symbol = ANOTHER CODE

ただこのままでは実行例のように警告が出てしまいます。これを抑制する(間違った)方法の一つにシンボルをシンボルテーブルから削除する方法があります。

undef *main::symbol; # シンボルを削除
*main::symbol = sub {
    return 'ANOTHER CODE';
};

実行例

% perl -w symbol.pl
symbol = CODE
symbol = ANOTHER CODE

ところで、Perlのシンボルは複数の型を同じ名前で入れることが出来ます。(GLOB)

our $symbol = 'SCALAR';

sub symbol {
    return 'CODE';
}

printf "\$symbol = %s\n", $symbol;
printf "symbol() = %s\n", symbol();

実行例

% perl -w symbol.pl
$symbol  = SCALAR
symbol() = CODE

お気づきの方もいらっしゃると思いますが、ここでシンボルを削除するとサブルーチン以外のものも削除されてしまうのです。

our $symbol = 'SCALAR';

sub symbol {
    return 'CODE';
}

undef *main::symbol;

printf "\$symbol = %s\n", $symbol;
printf "symbol() = %s\n", symbol();

実行例

% perl -w symbol.pl
Use of uninitialized value in printf at symbol.pl line 9.
$symbol =
Undefined subroutine &main::symbol called at symbol.pl line 10.

また、サブルーチンのみを削除しようと

undef *main::symbol{CODE};

と書くことはできません。

% perl -w symbol.pl
Can't modify glob elem in undef operator at symbol.pl line 7, near "};"
Execution of symbol.pl aborted due to compilation errors.


結局、サブルーチンを上書きする時にredefine警告を抑制する唯一の(正しい)方法はno warningsで警告そのものを抑制することです。
サブルーチンを上書きする時にPerl::Critic v1.051 -severity 1が通るredefine警告を抑制する方法は以下のようになります。

no warnings 'redefine';    ## no critic (TestingAndDebugging::ProhibitNoWarnings)
*main::symbol = sub {
    return 'ANOTHER CODE';
};
undef &main::symbol;    ## no critic (Subroutines::ProhibitAmpersandSigils)
*main::symbol = sub {
    return 'ANOTHER CODE';
};



実は自分自身も何かまずいなあと思いつつ何がまずかったのか思い出せずに、警告抑制の目的でundefでシンボルを削除してしまったことが時々ありました。これからは気を付けます。という戒めエントリーなのです。