@_とはなんなのか

id:perlcodesampleさんからコメントをもらったので整理するためまとめてみる。

まず@_ってそもそもなんなのか。
引数の入った配列らしいのでそれを検証。

#!/usr/bin/perl
use strict;
use warnings;


my @array = ("1", "2", "3", "4", "5");

arg1(@array);

my $str1 = 123;
my $str2 = "ABC";
arg2($str1, $str2);


sub arg1
{
	my $cnt = @_;
	print ("in arg1 cnt -> $cnt\n");
}

sub arg2
{
	my $cnt = @_;
	print ("in arg2 cnt -> $cnt\n");
}
C:\MyWorks\Perl\arg>perl arg11.pl
in arg1 cnt -> 5
in arg2 cnt -> 2

ちゃんと配列だった。

で、配列の要素はスカラーなので1つ1つにアクセスする場合は$を付けろと。

#!/usr/bin/perl
use strict;
use warnings;


my @array = ("1", "2", "3", "4", "5");

arg1(@array);

my $str1 = 123;
my $str2 = "ABC";
arg2($str1, $str2);


sub arg1
{
	my $cnt = @_;
	my $arr0 = $_[0];
	my $arr1 = $_[1];
	print ("in arg1 cnt -> $cnt\n");
	print ("in arg1 arr0 -> $arr0\n");
	print ("in arg1 arr1 -> $arr1\n");
}

sub arg2
{
	my $cnt = @_;
	my $str0 = $_[0];
	my $str1 = $_[1];
	print ("in arg2 cnt -> $cnt\n");
	print ("in arg2 str0 -> $str0\n");
	print ("in arg2 str1 -> $str1\n");
}
C:\MyWorks\Perl\arg>perl arg12.pl
in arg1 cnt -> 5
in arg1 arr0 -> 1
in arg1 arr1 -> 2
in arg2 cnt -> 2
in arg2 str0 -> 123
in arg2 str1 -> ABC

ここまではOK。

次に

@_の中身に入っているスカラーエイリアスなのであって、@_自体はエイリアスじゃないです。

これが問題。
いまいち理解できなかったけど別名だから同じものを指してると言う意味でいいのかな。要はCで言えば@_の中身が引数と同じアドレスを指してるってことのはず。
だとすれば引数のアドレスとサブルーチンの中の@_は同じアドレスを指していて、myで代入した値は@_の値のコピーなのでアドレスは別になるはず。
というわけで実験。

#!/usr/bin/perl
use strict;
use warnings;


my $str = "123";

print "str-> $str\n";
print "str-> " . \$str . "\n";
arg1($str);
print "str-> $str\n";

sub arg1
{
	my $str = $_[0];
	print "\$_[0]-> " . \$_[0] . "\n";
	print "str-> " . \$str . "\n";
	print ("in arg1 str -> $str\n");
	$str = "ABC";
	$_[0] = "XYZ";
}
C:\MyWorks\Perl\arg>perl arg13.pl
str-> 123
str-> SCALAR(0x18328bc)
$_[0]-> SCALAR(0x18328bc)
str-> SCALAR(0x1853648)
in arg1 str -> 123
str-> XYZ

予想通りの結果。
サブルーチンの引数のアドレスと、サブルーチン内の引数のアドレスが同じ。
コピーした値を変更しても変わらないけど、引数自体を変更したらアドレスが同じだから変わると。
要はサブルーチンの引数自体は参照渡しであって、ローカル変数に代入した時点で値渡しみたいになると。
だけどid:perlcodesampleさんの言ってる意味と同じ認識でいいのかなあ。

配列でも同様のはず。

#!/usr/bin/perl
use strict;
use warnings;


my @array = ("1", "2", "3", "4", "5");

print "array-> @array\n";
print "array[0]-> " . \$array[0] . "\n";
print "array[1]-> " . \$array[1] . "\n";
arg1(@array);
print "array-> @array\n";

sub arg1
{
	my $cnt = @_;
	print ("in arg1 cnt -> $cnt\n");
	my $str0 = $_[0];
	my $str1 = $_[1];
	print "\$_[0]-> " . \$_[0] . "\n";
	print "\$_[1]-> " . \$_[1] . "\n";
	print "str0-> " . \$str0 . "\n";
	print "str1-> " . \$str1 . "\n";
	$str0 = "ABC";
	$str1 = "DEF";
	$_[0] = "UVW";
	$_[1] = "XYZ";

}
C:\MyWorks\Perl\arg>perl arg14.pl
array-> 1 2 3 4 5
array[0]-> SCALAR(0x275f50)
array[1]-> SCALAR(0x276004)
in arg1 cnt -> 5
$_[0]-> SCALAR(0x275f50)
$_[1]-> SCALAR(0x276004)
str0-> SCALAR(0x18534a8)
str1-> SCALAR(0x185349c)
array-> UVW XYZ 3 4 5

予想通り。

で、問題は@_にリストを代入した場合にそのアドレスが変わっていれば

 サブルーチンを呼んだときに、@_の中身が設定されるので、@_自体を @_ = ( 1, 2 ) 見たいに置き換えてあげても、元の値は変更されませんよ。これは ( 1, 2 )という新しいリストをメモリ上に作成してあげて@_に代入するという意味です。

これの意味がわかるはず。
というわけで実験。

#!/usr/bin/perl
use strict;
use warnings;


my @array = ("1", "2", "3", "4", "5");

print "array-> @array\n";
print "array[0]-> " . \$array[0] . "\n";
print "array[1]-> " . \$array[1] . "\n";
arg1(@array);
print "array-> @array\n";

sub arg1
{
	my $cnt = @_;
	print ("in arg1 cnt -> $cnt\n");
	print "\$_[0]-> " . \$_[0] . "\n";
	print "\$_[1]-> " . \$_[1] . "\n";

	@_ = ("A", "B", "C", "D", "E");
	print "\$_[0]-> " . \$_[0] . "\n";
	print "\$_[1]-> " . \$_[1] . "\n";

	$_[0] = "UVW";
	$_[1] = "XYZ";

}
C:\MyWorks\Perl\arg>perl arg15.pl
array-> 1 2 3 4 5
array[0]-> SCALAR(0x275f50)
array[1]-> SCALAR(0x276004)
in arg1 cnt -> 5
$_[0]-> SCALAR(0x275f50)
$_[1]-> SCALAR(0x276004)
$_[0]-> SCALAR(0x185355c)
$_[1]-> SCALAR(0x1853550)
array-> 1 2 3 4 5

予想通り。

	@_ = ("A", "B", "C", "D", "E");

これやった時点で新しくリストが作成されて@_に代入され、元のアドレスが変わってしまうため書き換えたところで変わらないと。

てことで検証できたと思うんですがどうですか?