これは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'); # 'doing stuff at once'
doit(:when<noon>, :what('more stuff')); # 'doing more stuff at noon'
# 不正: doit("stuff", "now")
DESCRIPTION
サブルーチンはsub
キーワードで宣言され、CやJavaその他の言語と同様に形式的パラメータを持ちます。これらのパラメータは型制約をとることもできます。
パラメータはデフォルトでは読み出し専用です。この挙動はいわゆる「Trait」で変更できます:
sub foo($bar) {
$bar = 2; # 禁止されている
}
my $x = 2;
sub baz($bar is rw) {
$bar = 0; # 許される
}
baz($x); say $x; # 0
sub quox($bar is copy){
$bar = 3;
}
quox($x); say $x # まだ0
パラメータは?
を後ろに付けたり、デフォルト値を与えることでオプションにできます。
sub foo($x, $y?) {
if $y.defined {
say "WE CAN HAZ $y"
}
}
sub bar($x, $y = 2 * $x) {
...
}
名前付きパラメータ
my_sub($first, $second)
のようにしてサブルーチンを呼び出すとき、$first
は最初の形式的パラメータに、$second
は2番目の形式的パラメータに、といった具合に結びつきます。このようなパラメータを「位置的」パラメータと呼んでいます。
数を数えるより名前を思い出す方が簡単なことは往々にしてあるので、Perl6は名前付きパラメータも持っています:
my $r = Rectangle.new(
x => 100,
y => 200,
height => 23,
width => 42,
color => 'black'
);
引数が具体的に何を意味しているか、見るなりすぐに理解できます。
名前付きパラメータを定義するには、単にパラメータの前に:
を置くだけです:
sub area(:$width, :$height) {
return $width * $height;
}
area(width => 2, height => 3);
area(height => 3, width => 2 ); # 同じ
area(:height(3), :width(2)); # 同じ
ここまでの例では変数名がパラメータの名前としても使われていますが、別の名前を使うこともできます:
sub area(:width($w), :height($h)){
return $w * $h;
}
area(width => 2, height => 3);
名前付き引数は名前を使ってのみ渡すことができ、位置による指定はできません。一方で位置的引数は名前を使って渡すこともできます:
sub sqrt($number) { ... };
sqrt(3);
sqrt(number => 3); # これも動く
丸呑み(Slurpy)引数
サブルーチンにシグネチャを付けることは、前もって引数の数を知っておかなければならないということではありません。 残された引数を使い切る、丸呑み(slurpy)パラメータを(通常のパラメータの後に)定義することができます:
sub tail ($first, *@rest){
say "First: $first";
say "Rest: @rest[]";
}
tail(1, 2, 3, 4); # "First: 1\nRest: 2 3 4\n"
変数展開
デフォルトでは配列は引数リストに展開されません。したがってPerl5とは異なり次のようなコードが書けます:
sub a($scalar1, @list, $scalar2){
say $scalar2;
}
my @list = "foo", "bar";
a(1, @list, 2); # 2
これはデフォルトではリストを引数リストとして使えないということでもあります:
my @indexes = 1, 4;
say "abc".substr(@indexes) # エラー!
プレフィクス|
を付けると期待した動作をします:
say "abcdefgh".substr(|@indexes) # bcde
多重サブルーチン
異なるパラメータリストを持つ同名のサブルーチンを複数定義できます:
multi sub my_substr($str) { ... } # 1
multi sub my_substr($str, $start) { ... } # 2
multi sub my_substr($str, $start, $end) { ... } # 3
multi sub my_substr($str, $start, $end, $subst) { ... } # 4
このようなサブルーチンを呼び出すと、パラメータリストが一致するものが選ばれます。
多重サブルーチンは引数の数で区別できる必要はなく、パラメータの型でも区別します:
multi sub frob(Str $s) { say "Frobbing String $s" }
multi sub frob(Int $i) { say "Frobbing Integer $i" }
frob("x") # Frobbing String x
frob(2) # Frobbing Integer 2
MOTIVATION
明示的な型シグネチャの有用性を疑う人はいません: タイピング量は削減され、重複引数の確認は省け、より自己文書化されたコードになります。 名前付きパラメータの価値も既に議論され切っています。
これはまた有用なイントロスペクションを提供します。例えばブロックかサブルーチンをArray.sort
に渡したとして、そのコードが1つだけ引数を取るとすると、シュワルツ変換が自動的に行われます——このような機能はPerl5では不可能でした。明示的なシグネチャを欠いているため、sort
はコードブロックが何個の引数を取るのか判断できないからです。
多重サブルーチンは組み込み関数を新しい型に対してオーバーライドできるという点で非常に便利です。例えばあなたが(大文字と小文字の変換に独特の規則がある)トルコ語の文字列を正しく扱えるようにローカライズされたバージョンのPerl6を欲しがっているとしましょう。
言語を改変する代わりに単にTurkishStrという新しい型を導入し、組み込み関数に多重サブルーチンを追加するだけです:
multi uc(TurkishStr $s) { ... }
これで文字列が言語に対応した型を持っているかだけ気をつければ、uc
を通常の組み込み関数のように使えます。
演算子もサブルーチンなので、これらの改善点は演算子についても同様に働きます。
コメント
コメントを投稿