Unknown::Programming このページをアンテナに追加 RSSフィード

2007-12-25 注意

whileでファイルハンドルをループする時の暗黙の$_について

こんな記事をみつけた。

Perlプログラムをコンパクトに記述する方法 - builder by ZDNet Japan

短く書けてPerlってすげーんだよ的な記事でまぁそれはそれでいいんだけどこういう場合はデメリットもちゃんと説明すべきかと。

というのも上記記事にこういうコードがあったのです。


while (<STDIN>) {
    print $_;
}

$_を使うことによって「while( $line = <STDIN> ) {}」と書くよりも短く書けて良いっちゃ良いんですが、このコードにはバグが含まれています。

whileでの暗黙の$_はローカル化されないのです。コレ超重要。

つまり、


$_ = 100;

while ( <STDIN> ) {
    print $_;
}

print $_;

こう書くと$_に格納してた100が上書きされてしまうので、whileを抜けたあとに$_を参照しても100という値は消えてなくなっちゃうというわけです。

ちなみにforeachの場合は暗黙の$_がローカル化されるので問題ありません。


$_ = 100;

my @array = qw/a b c d/;

foreach (@array) {
    print $_;
}

print $_; # ちゃんと100と表示される

これは大きな違いです。暗黙の$_をforeachのノリでwhileでも使っちゃうと思わぬところでバグが出ます。

こういう挙動をするということを知ってて使うのと知らないで使うのとでは雲泥の差があります。

ちょっとしたプログラムを書く場合は僕も$_使ったりして短くさくっと書いたりしますが、ちゃんとしたプログラム書くときはやはり普通に変数で受け取って処理するのが吉です。


while ( my $line = <STDIN> ) {
    print $line;
}

どうしてもwhileで$_を使いたい場合は、暗黙の$_を使わずに明示的にローカル化すれば一応安全に使えます。


while ( local $_ = <STDIN> ) {
    print $_;
}

ってことでみんな$_の扱いには注意しようね!


追記

具体例があったほうがいいかなと思ったので追記


sub print_file {
    my $file = shift;
    open my $FH , $file or die;
    while ( <$FH> ) {
        print $_;
    }
    close $FH;
}

foreach (qw/ hoge.txt muge.txt / ){
    print_file($_);
}

これ、実行するとエラー吐いて死にます。

何故死ぬかは宿題ね!

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証