Perl のサブルーチン引数といえば実引数への参照を保持する特殊配列 @_
を手続き的に分解するのが長らくの伝統だった。これはシェルの特殊変数 $@
に由来する意味論で、おそらく JavaScript の arguments
変数にも影響を与えている。
すべての Perl サブルーチンはプロトタイプ宣言がない限りリスト演算子なので、この流儀は一種合理的でもあるのだが、実用的にそれで良いかというとまったくそうではないという問題があった; 結局大多数のサブルーチンは定数個の引数を取るので、それを参照する形式的パラメータが宣言できる方が都合が良いのである。
そういうわけで実験的に導入されたサブルーチンシグネチャ機能により形式的パラメータが宣言できるようになったのは Perl 5.20 からである。その後 Perl 5.28 において出現位置がサブルーチン属性の後に移動したことを除けば Perl 5.34 リリース前夜の今まで基本的に変わっておらず、未だに実験的機能のままである。
おまじない
シグネチャは前方互換性を持たない (構文的にプロトタイプと衝突している) 実験的機能なのでデフォルトでは無効になっている。
そのため明示的にプラグマで利用を宣言しなければならない:
use feature qw/signatures/;
no warnings qw/experimental::signatures/;
どの途みんな say
関数のために使うので feature プラグマは問題ないだろう。実験的機能を断りなしに使うと怒られるので、no warnings
で確信犯であることをアピールする必要がある。
これでプラグマのスコープにおいてサブルーチンシグネチャ (と :prototype
属性; 後述) が利用可能になり、従来のプロトタイプ構文が無効になる。
使い方
対訳を載せておく。シグネチャの方は実行時に引数チェックを行うので厳密には等価でないことに注意:
# | Old School | use feature qw/signatures/ |
---|---|---|
1 | sub f { my ($x) = @_; ... } |
sub f($x) { ... } |
2 | sub f { my ($x, undef, $y) = @_; ... } |
sub f($x, $, $y) { ... } |
3 | sub f { my ($x, $y) = @_; $y //= ...; ... } |
sub f($x, $y = ...) { ... } |
4 | sub f { my ($x, @rest) = @_; ... } |
sub f($x, @rest) { ... } |
5 | sub f { my ($x, %rest) = @_; ... } |
sub f($x, %rest) { ... } |
6 | sub f(&@) { my ($f, @args) = @_; ... } } |
sub f :prototype(&@) ($f, @args) { ... } |
なんのことはない、今までプログラマがやっていた @_
の分解を自動でやってくれるようになっただけである。
1, 2 は定数個の形式的パラメータを持つ。互換性やら API の一貫性やらのために使われない形式的パラメータが必要な場合は良くあるが、その場合 2 のように変数名を省略できる。
3 はオプショナル引数を持っている。当然だがオプショナル引数は形式的パラメータ列の右側に寄せる必要がある; sub ($x = 42, $y) { ... }
のように右に必須引数を残してより左の形式的パラメータをオプショナルにはできない。 またオプショナル引数のデフォルト値は Python と同じく呼び出し毎に解決される; 元々 $x //= calc_default();
などとやっていたのと同じと思って良い。
これら有限個の形式的パラメータを持つシグネチャが宣言された場合、単に変数の初期化だけでなくその個数も確認される。つまり例の 1 と 2 はそれぞれ厳密に 1 個と 3 個、3 は 1 個以上 2 個以下の実引数で呼び出されなければならない。それより多かったり少なかったりすると実行時例外が発生する (プロトタイプと違いコンパイル時エラーにはならない。) 旧来の方法のように超過した実引数を単に無視したい場合は、後述する slurp 引数と 2 の無名パラメータを組み合わせることができる: sub f($x, @) { ... }
4, 5 はリスト演算子である。任意個の実引数を取り、形式的パラメータからあぶれた値は最後に置かれた配列ないしハッシュが丸飲みする。これを Raku と同じく slurp 引数と呼ぶ。 要するに Python の *args
とか **args
と同じものだが、Perl 5 は名前付き引数と位置的引数の意味論上の違いを持たない (e.g., foo => 'bar'
は qw/foo bar/
の構文糖衣に過ぎない) ので一つのシグネチャが配列とハッシュ両方の slurp 引数を持つことはできない。 またハッシュの場合はそれが引き受けた実引数が偶数個でない場合に実行時例外が発生する。
6 はプロトタイプと同時に宣言する場合である。プロトタイプにはサブルーチン呼び出しの統語論や意味論を変える特殊な宣言がある; 例えばプロトタイプで第一引数に &
を宣言したサブルーチンは組み込み関数の eval
/grep
/map
/sort
のようにブロックを無名サブルーチンへのリファレンスとして受け取るという文法上の特殊規則があるが、シグネチャにそのような機能はない。そのため両方を宣言する必要がある。 先述したようにプロトタイプの本来の構文 (サブルーチン名 (あれば) の後に ()
で囲んで宣言する) はシグネチャと衝突し得るので use feature qw/signatures/
すると使えなくなる。その代替として :prototype
属性がインポートされるので、サブルーチンにこの属性をつけることでプロトタイプを定義できる。Perl 5.28 以降シグネチャはサブルーチン属性より後に順番が入れ替わっているので注意。ちなみに本来のプロトタイプはサブルーチン属性より前に出現する。
なおプロトタイプとシグネチャの整合性は検査されない。矛盾する記述をした場合はコンパイル時エラーと実行時例外でどうやっても呼べないサブルーチンになる。
最後に、いずれの場合にも @_
は依然として存在し、すべての実引数にアクセス可能である。
コメント
コメントを投稿