これはMoritz Lenz氏のWebサイトPerlgeek.deで公開されているブログ記事"Perl 5 to 6" Lesson 13 - Custom Operatorsの日本語訳です。
原文は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 13 - カスタム演算子
SYNOPSIS
multi sub postfix:<!>(Int $x) {
my $factorial = 1;
$factorial *= $_ for 2..$x;
return $factorial;
}
say 5!; # 120
DESCRIPTION
演算子は変わった名前を持ち、優先度とか結合性のような付加的な属性が少しだけ付いた関数です。
Perl6は通常term infix termというパターンに従います。termは前置演算子が前に付いていたり、後置演算子や後置接周(postcircumfix)演算子が後に付いたりしていても構いません。
1 + 1 中置
+1 前置
$x++ 後置
<a b c> 接周
@a[1] 後置接周
演算子の名前は「特別な」文字に限らず、空白以外なら何でも使えます。
演算子の長い名前はそのタイプの後にコロンとリテラルあるいはシンボルのリストが付きます。
例えばinfix:<+>は1+2で使われている演算子です。
もう一つの例はpostcircumfix:<[ ]>で、これは@a[0]の演算子です。
これまで得た知識を使えば、もう新しい演算子を定義できます:
multi sub prefix:<€> (Str $x) {
2 * $x;
}
say €4; # 8
優先度
$a + $b * $cのような式において、infix:<*>演算子はinfix:<+>演算子よりきつい優先度を持ちます。
これが式が$a + ($b * $c)と解釈される理由です。
新しい演算子の優先度は既存の演算子との比較で与えることができます:
multi sub infix:<foo> is equiv(&infix:<+>) { ... }
mutli sub infix:<bar> is tighter(&infix:<+>) { ... }
mutli sub infix:<baz> is looser(&infix:<+>) { ... }
結合性
ほとんどの中置演算子は引数を2つだけ取ります。1 / 2 / 4のような式では、評価の順序は結合性に拠って決められます。
infix:</>演算子は左結合なので、この式は(1 / 2) / 4と解析されます。
infix:<**>(べき乗)のように右結合の演算子の場合、2 ** 2 ** 4は2 ** (2 ** 4)と解析されます。
Perl6には更に多くの結合性があります: noneは同じ優先度の演算子の結合を禁止します(例えば2 <=> 3 <=> 4は禁止されています)。
またinfix:<,>はリスト結合性を持ちます。1, 2, 3はinfix:<,>(1; 2; 3)と解釈されます。
最後に連結結合性があります: $a < $b < $cは($a < $b) && ($b < $c)と解釈されます。
multi sub infix:<foo> is tighter(&infix:<+>)
is assoc('left')
($a, $b) {
...
}
後置接周と接周
後置接周演算子はメソッド呼び出しです:
class OrderedHash is Hash {
method postcircumfix:<{ }>(Str $key) {
...
}
}
$object[$stuff]のようにして呼び出すと$stuffがメソッドの引数として渡され、$objectはselfとして参照可能になります。
接周演算子は通常と違った構文(my @list = <a b c>;のような)に使われることが多いのでマクロとして実装されています:
macro circumfix:«< >»($text) is parsed / <-[>]>+ / {
return $text.comb(rx/\S+/);
}
is parsedトレイトには区切り子の中の文字列を解析する正規表現を与えます。もし指定しない場合は通常のPerl6コードとして解析されます(新しい構文を導入したいときには、これは望む動作ではないでしょう)。
Str.combはパターンを探し、マッチしたテキストのリストを返します。
既存の演算子の「オーバーロード」
(全部ではないにしろ)既存のほとんどの演算子は多重サブルーチンかメソッドであり、新しい型に合わせてカスタマイズできます。 多重サブルーチンを追加することで演算子の「オーバーロード」が実現されます。
class MyStr { ... }
multi sub infix:<~>(MyStr $this, Str $other) { ... }
これは組み込みの「特別な」オブジェクト(StrやIntなど)のようにふるまうオブジェクトを書けるということです。
MOTIVATION
ユーザによる新しい演算子の定義や、既存の演算子の「オーバーロード」を許すことは、ユーザ定義型を組み込み型と同じくらい強力で便利なものにします。 もし組み込み型では機能不足になったとしても、コンパイラに変更を加えることなく新しい型に置き換えて状況に対応できます。
これは言語を使うことと言語を改変することのギャップをも取り払います。
SEE ALSO
http://perlcabal.org/syn/S06.html#Operator_overloading
もし技術的な背景(Perl6がこのような演算子その他の変更をどうやって実現しているかなど)に興味があるなら、http://perlgeek.de/en/article/mutable-grammar-for-perl-6を読んで下さい。
コメント
コメントを投稿