ルモーリン

特殊変数「$_」の更新に注意

投稿:2017-02-12

何年も前に特殊変数$_の内容が意図通りにならず頭を抱えて対症療法で済ましていました。これが久しぶりに再発したので原因をキチンと調べて再発防止を図ります。
ここに書いてありました。
「$_ はグローバル変数です。」
Perlの組み込み変数 $_ の翻訳 - perldoc.jp
干支の酉に☆をマークするサブルーチンを2つ書きました。 eto_mark_1は正常、eto_mark_2はforループの$_を上書きしてしまいます。 私の場合、そのサブルーチン内でforループしたケースもあって稀なケースでした。 まあこれで原因と対策が判明しましたから再発しないことでしょう。
#!/usr/bin/perl -w

use utf8;
use strict;
use warnings;
use open IO => ":utf8";

use Encode::Locale;

binmode STDOUT, ":encoding(console_out)";

$| = 1;

my @eto = qw / 未 申 酉 戌 亥 /;
print "ループ前:@eto\n";
for (@eto) {
	print "コール前:$_\n";
	eto_mark_2($_);
	print "コール後:$_\n";
}
print "ループ後:@eto\n";

exit;

sub eto_mark_1 {
	my ($eto) = @_;
	$eto .= "☆" if $eto =~ /酉/;
	print "コール中:$eto\n";
}

sub eto_mark_2 {
	my ($eto) = @_;
	$_ = $eto;
	s/酉/酉☆/;
	$eto = $_;
	print "コール中:$eto\n";
}
始めに正常ケースeto_mark_1の結果です。まあ意図通りに☆マークがついてますね。
ループ前:未 申 酉 戌 亥
コール前:未
コール中:未
コール後:未
コール前:申
コール中:申
コール後:申
コール前:酉
コール中:酉☆
コール後:酉
コール前:戌
コール中:戌
コール後:戌
コール前:亥
コール中:亥
コール後:亥
ループ後:未 申 酉 戌 亥
次に失敗ケースeto_mark_2の結果です。 マークを付けた相手はサブルーチンの呼び出し元のforループの配列の要素です。 なのでループ後のリストも酉☆になりました。
ループ前:未 申 酉 戌 亥
コール前:未
コール中:未
コール後:未
コール前:申
コール中:申
コール後:申
コール前:酉
コール中:酉☆
コール後:酉☆
コール前:戌
コール中:戌
コール後:戌
コール前:亥
コール中:亥
コール後:亥
ループ後:未 申 酉☆ 戌 亥
  • 特殊変数$_を自分で更新してバグが出ない安全地帯(?)は、mainパッケージ(素のスクリプト)のサブルーチンの外やforの外
  • サブルーチンの中で更新すると呼び出し元の$_が更新される
  • forの中で更新するとforの配列の要素が更新される
  • パッケージの中で更新すると元の処理がてんやわんやになる<なんだそりゃ?