以下のコードにおけるwhileとforeachでの$_の使いかたについて教えてください。

my @foos = qw( a b );

sub foo1 {
  open FH, "foo.txt";

  while (<FH>) {
    chomp;
    print "$_";
  }

#  while (my $x = <FH>) {
#    chomp $x;
#    print "$x";
#  }
  
  print "\n";
}

sub foo2 {
  foreach (@foos) {
    print "<$_>";
    foo1();
  }
  print "\n";
}

foo2();
foo2();

このコードと同じディレクトリに以下のような内容のfoo.txtをおいて実行します。

x
y

すると、以下のような結果が得られました。

<a>xy
<b>xy

<>xy
<>xy

一回目のfoo2で@foosの内容が消えてしまいます。一方、コメントアウトしてあるようにwhileに変数($x)を使うと期待したとおりの結果が出ます。なぜ、このような結果になるのでしょうか。ご存知の方がいらっしゃったら教えてください。宜しくお願いします。ちなみに、WinXP + ActivePerl(v5.8.6)で試しました。

A 回答 (2件)

簡単に言うと


$_ をfoo1 とfoo2 で使っている為です。
こういうことがあるので
関数の中では使う変数はできるだけ局所的な使い方をするのがよろしいです。
最初の
foreach(@foos){
}
の中で$_ は、配列の中身のコピーではなくて、配列の要素そのものを指しているので、$_ への変更は直接@foosへの変更になってしまうので注意が必要です(この動作はこの動作で便利なものなんですが・)
なので、
foreach my $x (@foos) {
print "<$x>";
foo1();
}
のようにすれば良かったのかもしれません。
また、
sub foo1{
local $_;

}
のようにすれば、
foo1 で(foo2で使っていても)安心して$_ を使うことができます。
    • good
    • 0
この回答へのお礼

whileの終了条件のundefが@foosに代入されていたのでしょうね。$_がグローバル変数であることと、$_を変更すると元のデータが変更されることを見落としていました。ご回答、ありがとうございました。

お礼日時:2006/01/06 01:23

ご指摘のように他のループ変数に入れるか、もしくは



sub foo1 {
  open FH, "foo.txt";
  local $_;#明示的にローカル化する
  while (<FH>) {
    chomp;
    print "$_";
  }
  print "\n";
}

とする必要があります。参考URLの I/O Operators の項をご覧下さい。

参考URL:http://perldoc.jp/docs/perl/5.6.1/perlop.pod
    • good
    • 0
この回答へのお礼

localで解決しました。$_を使ったほうが正規表現等の引数を省略できて便利なことも多いので、localを用いた方法を教えていただいて勉強になりました。ありがとうございました。

お礼日時:2006/01/06 01:25

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!


人気Q&Aランキング

おすすめ情報