perlsubクイズ
対象は自分です。
問題1
use strict; use warnings; use feature 'say'; # ↑以降省略 say do { my $a = 100; sub { $_[0] *= $_[0] }->($a); $a; };
問題2
say do { my @a = (1..10); sub { $_[0] *= $_[0] }->(@a); $a[0]; };
答え合わせ
問題1
A. 10000
理由
ルーチンに渡されるすべての引数は配列 @_ に置かれます。 したがって、ある関数を二つの引数を付けて呼び出したならば、 その引数は $_[0] と $_[1] に格納されます。 配列 @_ は local 配列ですが、その要素は実際の スカラパラメータの別名です。 たとえば $_[0] が更新された場合、対応する引数が更新されます (更新できない場合にはエラーとなります)。
perlsub - Perl のサブルーチン - perldoc.jp
備考
題意は以下と同等です。
say do { my $a = 100; sub { \$_[0] eq \$a }->($a) ? 'yes' : 'no'; };
問題2
A. 1
答えは合ってますが、題意は誤りでした(1*1=1なのは当たり前でした…)。
理由
引数が、配列やハッシュの(関数が呼び出された時点では存在してない) 要素であった場合、その要素は(対応する別名が)修正されたり リファレンスが取られたときにのみ作成されます。 (以前の一部のバージョンの Perl では、この要素は代入が行われようが行われまいが 作成されていました。)
perlsub - Perl のサブルーチン - perldoc.jp
配列やハッシュの実体を渡した場合、すべての要素が@_に展開されます。この時、@_はcopy-on-writeみたいな状態になっているようです。
この部分についての解釈を別記事にまとめました。
perlsubの解釈について - $ cat /var/log/shin
備考
配列やハッシュの実体を渡すサンプルを以下に示します。
say do { my @a = (1..10); my %b = (1..10); sub { join(', ', @_) }->(@a, %b); }; # 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 7, 8, 9, 10, 5, 6
”5, 6"が最後にきているのは*1、ハッシュは格納後の要素順が不定なためです。
配列とハッシュを明示的に渡すにはリファレンスを使います。
Perl での関数呼び出しと戻り値のモデルは単純です。全ての関数は引数を、 平坦で一つのスカラのリストで受け取り、同様に全ての関数は呼び出し元に 対して平坦で一つのスカラのりストで返すというものです。 これらの呼び出しリストと戻り値リストにある全ての配列とハッシュは、 潰されてそのアイデンティティを失います。 しかし、これを避けるために常にリファレンスで渡すことができます。
perlsub - Perl のサブルーチン - perldoc.jp
*1:順番は環境によって変わるかもしれません