grep(/^$array1[$_]$/, @array2);は使えない?
[上に]
[前に]
[次に]
くろっくぁ
1999/11/30(火) 00:25:54
適切なタイトルを付けられませんでした。すみません。
こういう使い方は滅多にしないのですが、
気になったので質問させてください。
@array1 = (1,2,3);
@array2 = (5,9,1);
for (0..$#array1) {
print "\$_ マッチ\n" if grep(/^$array1[$_]$/, @array2);
}
for $i (0..$#array1) {
print "\$i マッチ\n" if grep(/^$array1[$i]$/, @array2);
}
としたとき本来なら
$_ マッチ
$i マッチ
と出力されるはずだと思うのですが、
$i マッチ
と出力されるだけなのです。
なぜなのでしょうか・・・。
教えてください。よろしくお願いします。
B-Cus
1999/11/30(火) 02:04:35
grep(/^$array1[$_]$/, @array2)
は、@array2 から要素を1つずつ取り出し、$_ に代入して
/^$array1[$_]$/ を評価するので、最初の $_ は破壊されます。
grep(/^$array1[$_]$/, @array2)
は、
grep { /^$array1[$_]$/ } @array2
と等価。それを踏まえた上で、デバッグのために print 文を加える。
@array1 = (1,2,3);
@array2 = (5,9,1);
for (0 .. $#array1) {
print "check \$array1[$_](=$array1[$_])\n";
grep {
print "\$_=$_ \$array1[\$_]=\$array1[$_]=$array1[$_]\n";
/^$array1[$_]$/ ? $_ : ();
} @array2;
}
と書き換える。実行結果は以下の通り。
check $array1[0](=1)
$_=5 $array1[$_]=$array1[5]=
$_=9 $array1[$_]=$array1[9]=
$_=1 $array1[$_]=$array1[1]=2
check $array1[1](=2)
$_=5 $array1[$_]=$array1[5]=
$_=9 $array1[$_]=$array1[9]=
$_=1 $array1[$_]=$array1[1]=2
check $array1[2](=3)
$_=5 $array1[$_]=$array1[5]=
$_=9 $array1[$_]=$array1[9]=
$_=1 $array1[$_]=$array1[1]=2
$_ と $array1[$_] を比べるわけなので、マッチしない。
ふじ
1999/11/30(火) 02:06:46
> grep(/^$array1[$_]$/, @array2);
この grep 文では @array2 の要素が順番に $_ に代入されて、
それから 第一引数 ( /^$array1[$_]$/ つまり $_ =~ /^$array1[$_]$/)
が評価されます。
だから、
$array1[$_]
は、
($array1[5], $array1[9], $array1[1] ) == (undef, undef, 2)
となるので、それぞれ
undef と 5
undef と 9
2 と 1
が評価の対象になるので、マッチしない、と。
# デフォルトを使いすぎるのは危険ですよ(^^;
ふじ
1999/11/30(火) 02:08:52
あら、かぶっちゃった。
# いつもながら丁寧な回答ですねえ。感心します>B-Cusさん
くろっくぁ
1999/11/30(火) 02:12:26
[[解決]]
なるほど、よくわかりました。
B-Cusさん、ふじさん、親切に説明していただき
どうもありがとうございました。
ふじ
1999/11/30(火) 02:17:35
# B-Cus さんのサンプルの実行結果を見れば明らかなのですが...
>最初の $_ は破壊されます。
破壊される、と言っても for 文内で使われている $_ が上書きされちゃう
訳ではなくて、 grep 内でのローカルな(my か?) $_ に値が代入されて
評価に使われる(から grep を抜けたら元の値に戻る)、
ということですね。
# 念のため。
mm
1999/11/30(火) 15:11:42
>grep 内でのローカルな(my か?) $_ に値が代入されて
grep内の $_ は、ダイナミックに参照できるので、my ではなく local だと思います。
それはともかく、foreach のループ変数が局所化されることは、
プログラミングPerlにも明記されてるんですが、
grep については見つけられなかったので、ちょっと試してみました。
$_ = 1;
p('out');
foreach (2) { p('foreach') };
p('out');
grep (p('grep'), (3));
p('out');
while (<DATA>) { chomp; p('while'); last; }
p('out');
sub p { print "$_[0]: $_\n"; }
__END__
4
===============
out: 1
foreach: 2
out: 1
grep: 3
out: 1
while: 4
out: 4
この結果から grep は foreach と同じですが、while では局所化されないようです。
$_ が参照となる場合には、配列本体がスコープから外れても
GCできないようになるのを防ぐために、
このような局所化が行われるのだと解釈したのですが、
識者のみなさんのご意見はいかがでしょうか?
B-Cus
1999/12/01(水) 03:12:48
> 破壊される、と言っても
失礼しました。「破壊」という表現は不適当でした。
> GC
GC って、Garbage Collection ですよね?
> 配列本体がスコープから外れてもGCできないようになるのを防ぐために、
というより、foreach/grep/map は、
- $_ をいじると元配列が書き変わるようにしたかった
- そのために local(*_)=変数 で局所化した
ってことじゃないでしょうか。例えば、
@a=(1,2,3);
foreach (@a){ $_ *=2 }
は、
@a=(1,2,3);
$i=0;
while ( $i<=$#a ){
local(*_)=\$a[$i];
$_ *= 2;
} continue {
$i++;
}
であると。
つまり局所化は、GC のためでなく、リファレンス機能を持たせたかったが
ための副作用では、ということです。
# perl のソースを読む能力はないので、全て想像です。
mm
1999/12/01(水) 14:25:23
>GC って、Garbage Collection ですよね?
そうです…スペリングに自信がなかったもので(^^;
>つまり局所化は、GC のためでなく、リファレンス機能を持たせたかったが
>ための副作用では、ということです。
なるほど、確かに $_ は、局所化しておかないと、危険ですね。
GCうんぬんというレベルの問題ではありませんでした。ありがとうございます。
しかし、local(*_)=\$a[$i]; っていうのは、my 変数のリファレンスを
型グロブに代入するのと同様に、理解不能なコードだなぁ…
それと、foreachの中では、$_は局所化されますが、@_はそのままですね。
(while, local では、@_も局所化されるので、これらは等価ではない)。
どちらも、ソース的に小細工してるってことかな…?
mm
1999/12/02(木) 00:24:27
すいません、以下のようにすれば、@_ は局所化されないで済みました。
これで、foreach と等価になりそう…
@_ = (0);
@a=(1,2,3);
$i=0;
while ( $i<=$#a ){
local($_);
*_=\$a[$i]; # 実用perlプログラミング 3.3.1 選択的別名定義
$_ *= 2;
push @_, $_;
print "(@_)\n";
} continue {
$i++;
}
print "@_\n";
[上に]
[前に]
[次に]