引数

リファレンス再び

前回のこれで

http://d.hatena.ne.jp/lolstep/20080629/1214734023

リファレンスが何たるものかを大雑把に理解したわけだけども、肝心の無名配列やハッシュをやってなかったが

http://d.hatena.ne.jp/perlcodesample/20080201/1201884772
http://d.hatena.ne.jp/perlcodesample/20080217/1203233280

ここ読んで大体理解したのでそれでよしとする。じゃないと先に進めない。

引数いろいろ

http://d.hatena.ne.jp/perlcodesample/20080604/1212591180

### 引数の3とおりの受け取り方
# (1) my $num = $_[0]
# (2) my $num = shift;
# (3-1) my ( $num1, $num2 ) = @_;
# (3-2) my @num_list = @_;

* 引数は、暗黙的に、@_ に代入されています。
* @_ から、引数を取り出して、変数に格納するには、上記の4つの方法があります。
* わたしは、リスト代入を使うことが多いです。引数がひとつのときは、shift関数を、よく使います。

@_ってなんぞと思って調べたら特殊変数らしい。
そういえばはるか昔にPerlを勉強しようと思った時、わけのわからない記号が多く意味不明すぎて断念したトラウマがよみがえってきた。

http://www.rfs.jp/sb/perl/02/07.html

特殊変数 @_ は渡された値のリファレンスになっています。ですから、 @_ の値を変更すると、引数として指定された変数の値も変更されることになります。

あ、そうなの?
だったらリファレンスを引数としてわたしたらリファレンスのリファレンスになるってこと?
ここでもCのポインタのポインタとかいうトラウマがよみがえってきたが気にしないことにしておく。

というわけで実験。

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


my $str = "ABC";
my @array = ("1", "2", "3", "4", "5");
my $hash = {
    Key1 => "AA",
    Key2 => "BB",
};

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

print "array-> @array\n";
arg2(@array);
print "array-> @array\n";


sub arg1
{
    # (1)で受け取ってみる
    my $str = $_[0];

    print ("in sub -> $str\n");

    # そして書き換え
    $_[0] = "XYZ";
}

sub argr2
{
    # (1)で受け取ってみる
    my @array = $_[0];

    print ("in sub -> @array\n");

    # そして書き換え
    $_[0] = ("A", "B", "C", "D", "E");
}
C:\MyWorks\Perl\arg>perl arg1.pl
Useless use of a constant in void context at arg1.pl line 41.
Useless use of a constant in void context at arg1.pl line 41.
Useless use of a constant in void context at arg1.pl line 41.
Useless use of a constant in void context at arg1.pl line 41.
str-> ABC
in sub -> ABC
str-> XYZ
array-> 1 2 3 4 5
Undefined subroutine &main::arg2 called at arg1.pl line 18.

無駄なことはやめろというエラーが豪快に返ってきたところをみると、どうやら配列やハッシュは@_の個々の引数にはなっていない模様。
スカラーだけは引数の個数分入るってことなのかなあ。

というわけで配列やハッシュを引数にする方法を探すと

http://d.hatena.ne.jp/perlcodesample/20080605/1212675666

こうやればいいらしい。

もう1回。

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


my $str = "ABC";
my @array = ("1", "2", "3", "4", "5");
my %hash = (
    Key1 => "AA",
    Key2 => "BB",
);

print "array-> @array\n";
arg1(@array);
print "array-> @array\n";

print ("hash -> $hash{Key1}\n");
arg2(%hash);
print ("hash -> $hash{Key1}\n");


sub arg1
{
    my @array = @_;

    print ("in sub -> @array\n");

    # そして書き換え
    @_ = ("A", "B", "C", "D", "E");
}

sub arg2
{
    my %hash = @_;

    print ("in sub -> $hash{Key1}\n");

    # そして書き換え
    @_ = (
        Key1 => "YY",
        Key2 => "ZZ",
    );

}
C:\MyWorks\Perl\arg>perl arg2.pl
array-> 1 2 3 4 5
in sub -> 1 2 3 4 5
array-> 1 2 3 4 5
hash -> AA
in sub -> AA
hash -> AA

今度はうまくいったが書き変わっていない。
そんな気は薄々してたけど、サブルーチン内で代入してる時点で値コピーになっているんだろうな。

次にリファレンスをわたしてみる。

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


my $str = "ABC";

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


sub arg1
{
    # (1)で受け取ってみる
    my $str = $_[0];

    print ("in sub -> $$str\n");

    # そして書き換え
    $$str = "QQQ";
    $_[0] = "XYZ";
}
C:\MyWorks\Perl\arg>perl arg3.pl
str-> ABC
in sub -> ABC
str-> QQQ

リファレンスをわたしたのでリファレンスのリファレンスに対して書き換えればいいのはわかってたけど、引数のほうを直で書き換えるのはどうすんだろうなあ。
$$_[0]とか無理だとわかってやってみたがやはりダメだった。
そんなことする必要がないのはわかってるがなんか気になる。

配列のリファレンス。

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


my @array = ("1", "2", "3", "4", "5");
my %hash = (
    Key1 => "AA",
    Key2 => "BB",
);

print "array-> @array\n";
arg1(\@array);
print "array-> @array\n";


sub arg1
{
    my @array = @_;

    print ("in sub -> $@array\n");

    # そして書き換え
    $array[0] = "A";
}
C:\MyWorks\Perl\arg>perl arg4.pl
array-> 1 2 3 4 5
in sub -> array
array-> 1 2 3 4 5

なにも考えずボーっと受けてたけど、リファレンスでわたしてるんだからスカラーで受けなきゃダメだった。

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


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

print "array-> @array\n";
arg1(\@array);
print "array-> @array\n";


sub arg1
{
    my $array = @_;

    print ("なぜかだめ -> @$array\n");

    my @array1 = ("1", "2", "3", "4", "5");
    my $ref_array = \@array1;
    print ("こっちはうまくいく -> @$ref_array\n");

    # そして書き換え
    $array[2] = "A";
}
C:\MyWorks\Perl\arg>perl arg5.pl
array-> 1 2 3 4 5
Can't use string ("1") as an ARRAY ref while "strict refs" in use at arg5.pl line 17.

なんかよくわからないエラーがでた。
ちなみにそこをコメントにすると一応意図した動作になる。

C:\MyWorks\Perl\arg>perl arg5.pl
array-> 1 2 3 4 5
こっちはうまくいく -> 1 2 3 4 5
array-> 1 2 A 4 5

ちゃんと値を書きかえられてるところをみると、リファレンスとしてわたっているのは間違いないと思うが一応確認。

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


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

print "array-> @array\n";
arg1(\@array);
print "array-> @array\n";


sub arg1
{
    my $array = @_;

    print ("@_\n");

}
C:\MyWorks\Perl\arg>perl arg6.pl
array-> 1 2 3 4 5
ARRAY(0x36db0)
array-> 1 2 3 4 5

ちゃんとリファレンスだった。

なんでか全くわからないので再度ここをみてみると

http://d.hatena.ne.jp/perlcodesample/20080604/1212591180

my $num = $_[0] # @_ の最初の要素をを指定
my $num = shift; # shift 関数で、 @_ の先頭を切り出す
my ( $num1, $num2 ) = @_; # リスト代入を使って、スカラー変数に代入
my @num_list = @_; # 配列に代入

「リスト代入を使って、スカラー変数」に代入と書いてあるのが妙に引っかかった。

http://d.hatena.ne.jp/perlcodesample/20080606/1212767708

この辺みてもやはりリスト代入とやらで受け取っている。

とりあえず使ってみる。

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


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

print "array-> @array\n";
arg1(\@array);
print "array-> @array\n";


sub arg1
{
    my ($array) = @_;

    print ("@_\n");

	print "array-> @$array\n";

}
C:\MyWorks\Perl\arg>perl arg7.pl
array-> 1 2 3 4 5
ARRAY(0x36db0)
array-> 1 2 3 4 5
array-> 1 2 3 4 5

おお取れた。

ただいまいち理解できない。

http://www.wafu.ne.jp/~ton/web/perl/perl_01.htm

# $home[0] = "aaa";   …1個ずつ代入
# @home = ("aaa" , "bbb" , "ccc");   …リスト代入
# ("aaa" , "bbb" , "ccc") = @home;   …配列からスカラー変数に値をセット
# ($aaa, $bbb) = ($bbb, $aaa); …値が入れ替えられる。なんと、こんなことがっ!

ここみてなんとなくイメージわいて来たけど、@_はリストだからスカラーで受けるときはリスト代入しないとダメってこと?

http://d.hatena.ne.jp/perlcodesample/20080605/1212675666

* 引数をサブルーチンに渡すときは、スカラーであろうと、配列であろうと、ハッシュであろうと、リストとして@_に代入されて、サブルーチンに渡されます。
* Perlのサブルーチンの特徴のひとつは、「可変個引数をリストとして受け取る」ということです。引数の個数をあらかじめ定義しなければならない言語( C や Java ) を学習した人から見ると、不思議なものに見えるかもしれません。

ということで、今回はリファレンスわたしにしたためスカラーで受け取らなきゃならなかったけど、これが配列をわたして配列でうけるのなら悩む必要は無かったってことなんだろうか。

とにかく今回はいまいち理解できてないかなあ。しかもまだ引数は終わってないし。
というわけで問題積み残しのまま次回へ続く。