スキップしてメイン コンテンツに移動

Perl のサブルーチンシグネチャ早見表

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 以降シグネチャはサブルーチン属性より後に順番が入れ替わっているので注意。ちなみに本来のプロトタイプはサブルーチン属性より前に出現する。

なおプロトタイプとシグネチャの整合性は検査されない。矛盾する記述をした場合はコンパイル時エラーと実行時例外でどうやっても呼べないサブルーチンになる。

最後に、いずれの場合にも @_ は依然として存在し、すべての実引数にアクセス可能である。

コメント

このブログの人気の投稿

Perl 5 to 6 - コンテキスト

2011-02-27: コメント欄で既に改訂された仕様の指摘がありました ので一部補足しました。 id:uasi に感謝します。 これはMoritz Lenz氏のWebサイト Perlgeek.de で公開されているブログ記事 "Perl 5 to 6" Lesson 06 - Contexts の日本語訳です。 原文は Creative Commons Attribution 3.0 Germany に基づいて公開されています。 本エントリには Creative Commons Attribution 3.0 Unported を適用します。 Original text: Copyright© 2008-2010 Moritz Lenz Japanese translation: Copyright© 2011 SATOH Koichi NAME "Perl 5 to 6" Lesson 06 - コンテキスト SYNOPSIS my @a = <a b c> my $x = @a; say $x[2]; # c say (~2).WHAT # Str() say +@a; # 3 if @a < 10 { say "short array"; } DESCRIPTION 次のように書いたとき、 $x = @a Perl5では $x は @a より少ない情報—— @a の要素数だけ——しか持ちません。 すべての情報を保存しておくためには明示的にリファレンスを取る必要があります: $x = \@a Perl6ではこれらは反対になります: デフォルトでは何も失うことなく、スカラ変数は配列を単に格納します。 これは一般要素コンテキスト(Perl5で scalar と呼ばれていたもの)及びより特化された数値、整数、文字列コンテキストの導入によって可能となりました。無効コンテキストとリストコンテキストは変更されていません。 特別な構文でコンテキストを強制できます。 構文 コンテキスト ~stuff 文字列 ?stuff 真理値 +stuff ...

Perl 5 to 6 - サブルーチンとシグネチャ

これはMoritz Lenz氏のWebサイト Perlgeek.de で公開されているブログ記事 "Perl 5 to 6" Lesson 04 - Subroutines and Signatures の日本語訳です。 原文は Creative Commons Attribution 3.0 Germany に基づいて公開されています。 本エントリには Creative Commons Attribution 3.0 Unported を適用します。 Original text: Copyright© 2008-2010 Moritz Lenz Japanese translation: Copyright© 2011 SATOH Koichi NAME "Perl 5 to 6" Lesson 04 - サブルーチンとシグネチャ SYNOPSIS # シグネチャなしのサブルーチン——Perl5風 sub print_arguments { say "Arguments:"; for @_ { say "\t$_"; } } # 固定引数の型指定付きシグネチャ sub distance(Int $x1, Int $y1, Int $x2, Int $y2) { return sqrt ($x2-$x1)**2 + ($y2-$y1)**2; } say distance(3, 5, 0, 1); # デフォルト引数 sub logarithm($num, $base = 2.7183) { return log($num) / log($base) } say logarithm(4); # 第2引数はデフォルトを利用 say logarithm(4, 2); # 明示的な第2引数 # 名前付き引数 sub doit(:$when, :$what) { say "doing $what at $when"; } doit(what => 'stuff', when => 'once'); # ...

Project Euler - Problem 27

問題 しばらく止まってましたが今日から再開。 原文 Considering quadratics of the form: n 2 + an + b, where |a| < 1000 and |b| < 1000 Find the product of the coefficients, a and b, for the quadratic expression that produces the maximum number of primes for consecutive values of n, starting with n = 0. 日本語訳 |a| < 1000, |b| < 1000 として以下の二次式を考える (ここで|a|は絶対値): n 2 + an + b n=0から始めて連続する整数で素数を生成したときに最長の長さとなる上の二次式の, 係数a, bの積を答えよ. 解答 最大探索範囲は-999 <= a <= 999、-999 <= b <= 999なので、およそ4,000,000通りの係数の組合せを試すことになります。組合せ毎に数列を生成して、それが素数か判定するわけですからたまりません。簡単な検討を加えて範囲を絞りましょう。 与えられた二次式をf(n)とおくと、f(0) = b、f(1) = a + b + 1です。 f(n)が長さ2以上の素数列を生成するならこれらは素数ですから、次のことがいえます: bは素数である a + b + 1は素数である b = 2のとき、aは偶数である それ以外のとき、aは奇数である 素数判定関数 is_prime には同じ引数が与えられることがよくあるのでメモ化しています。 #!/usr/bin/perl use strict; use warnings; use feature qw/say/; sub prime_seq_len($$) { my ($coeff_a, $coeff_b) = @_; my $len = 0; my $n = 0; $len++, $n++ while is_prime($n * ($n + $coeff_a) ...

Perl 5 to 6 - ツイジル

これはMoritz Lenz氏のWebサイト Perlgeek.de で公開されているブログ記事 "Perl 5 to 6" Lesson 15 - Twigils の日本語訳です。 原文は Creative Commons Attribution 3.0 Germany に基づいて公開されています。 本エントリには Creative Commons Attribution 3.0 Unported を適用します。 Original text: Copyright© 2008-2010 Moritz Lenz Japanese translation: Copyright© 2011 SATOH Koichi NAME "Perl 5 to 6" Lesson 15 - ツイジル SYNOPSIS class Foo { has $.bar; has $!baz; } my @stuff = sort { $^b[1] <=> $^a[1]}, [1, 2], [0, 3], [4, 8]; my $block = { say "This is the named 'foo' parameter: $:foo" }; $block(:foo<bar>); say "This is file $?FILE on line $?LINE" say "A CGI script" if %*ENV.exists('DOCUMENT_ROOT'); DESCRIPTION いくつかの変数にはツイジルという第2のシジルがあります。これは基本的にはその変数が「普通」ではないということです。違いはいくつかあり、例えばスコープの違いなどです。 オブジェクトのパブリックな属性とプライベートな属性がそれぞれ . と ! というツイジルを持つことは既に紹介しました; それらは通常の変数ではなく self に結びつけられています。 ツイジル ^ はPerl5で例外的に扱われていたケースを一般化します。次のように書けます # 注意: Perl5のコードです sort ...

Project Euler - Problem 18

問題 原文 Find the maximum total from top to bottom of the triangle 日本語訳 三角形を頂点から下まで移動するとき、その最大の合計値を求めよ。 解答 動的計画法 を使ってボトムアップで簡単に解くことができる問題です。 簡単のため、小さい三角形で考えることにします: 0: j 1: h i 2: e f g 3: a b c d 2行目の各点を頂点として、2行の小さい三角形が作れることが分かります。 上の例で言えば、(e, a, b)と(f, b, c)、(g, c, d)の3つです。 (e, a, b)の頂点eから末端(a、b、c、dのいずれか)に移動したとき、その数値の合計は最大でe + max(a, b)となります(maxは最大値を選ぶ関数)。同様に他の2つもf + max(b, c)、g + max(c, d)と表せます。 これらをE、F、Gとおくことにして、例を次のように書き換えます: 0: j 1: h i 2: E F G (h, E, F)からなる三角形の最大値はH = h + max(E, F)、(i, F, G)からなる三角形のそれはI = i + max(F, G)です。 Eは「頂点eから末端に至る経路の最大値」で、FやGも同様ですから、HとIは「頂点h(やi)から末端に至る経路の最大値」となります。 これを先ほどと同様に置き換えて: 0: j 1: H I 頂点jから末端に至る経路の最大値はJ = j + max(H, I)となり、これが解です。 #!/usr/bin/perl use strict; use warnings; use feature qw/say/; use List::Util qw/max/; my @rows = map { [ split /\s+/ ] } <DATA>; until (@rows == 1) { my $curr_row = $rows[-2]; my $bigger_branch; for (my $i = 0; $i < @$curr_row; $i++) { $bigger_branch = ma...