J言語が難読である要因は色々ありますが、演算子が激しくオーバーロードされていることも理由のひとつです。さらには、演算子記号を省略した併置(juxtaposition)がオーバーロードされていることは、プログラム(式)の解釈を相当に困難にしています。あまりにヒドイので、かえって笑っちゃいますよ。
併置とは、構文上は単に並べることです。日常的にお馴染みの例は掛け算です。a×x + b は、掛け算演算子記号「×」を省略して、ax + b と書いてもいいですよね。このとき、aとxの併置は掛け算演算子の省略と解釈します。別な言い方をすると、併置演算子が掛け算演算子の別記法として使われているのです。
プログラミング言語において併置がどんな意味を持つかは、たいていは天下りに決まっています。例外的に、CafeOBJ(マイナーな言語)では、併置演算もユーザー定義できます。「雑談しながら思ったこと」という記事のjmkさんのコメントによると、「juxtaposition operatorはFortressで使えます。」とのこと。Fortressもあまり一般化しなかったマイナー言語ですけど。
さて、J言語の場合ですが、併置の意味は言語仕様で決まっています。例えば、1次元配列(リスト)の平均値を求める例(有名らしい)は次のようになります。
average =: + / % #
average 5 8 7 5 11
7.2
ユーザー定義演算子averageは、「+」「/」「%」「#」という4つの記号の併置で構成されます。上の定義では、分かりやすいように空白をはさんで併置してますが、「+」「/」「%」「#」は演算子記号であり、それ自体で区切りとなるので、+/%# とくっつけて書いても同じです。名前を付ける必要も特にないので、次のようにインラインでいきなり書いてもOKです。
(+/%#)5 8 7 5 11
7.2
J言語は、「右から左に読み、演算子の優先順はない」と「J言語の印象: その変態さと可能性」で言いました。であるなら、+ / % # は、
- (+ (/ (% #)))
と解釈されそうです。残念ながら違います。「/」は普通の演算子より偉いメタ演算子なので、結合力が強いのです。しかも引数を左側からとります(普通の単項演算子は右側に引数)。+ / の部分を優先すると、
- ((+ /) (% #))
でしょうか? これも違います。
((+ /) (% #))5 8 7 5 11
6 6.6 6.4 6 7.2
9 9.6 9.4 9 10.2
8 8.6 8.4 8 9.2
6 6.6 6.4 6 7.2
12 12.6 12.4 12 13.2
演算子が複数並ぶとき、(f g) と (f g h) は特別な解釈をします。4つ以上並ぶときは、右からグループ化して解釈します。とにかくヤヤコシイのですが、+ / % # に関して言えば、「+/」「%」「#」を3つの引数とするメタ演算子(「フォーク」と呼ぶ)が併置として(つまり単に3つ並べることで)表現されています。
ちなみに、(+/%#)5 8 7 5 11 の括弧をはずして、+/%#5 8 7 5 11 とすると、まったく違った意味になります。
+/%#5 8 7 5 11
0.2
x, y はリテラル値、f, g, h が演算子(J言語の動詞)のとき、併置は次のような複数の解釈を持ちます。
- (x y) は、配列を作る。
- (f y) は、値yに、単項演算子fを適用。
- (x g y) は、値xとyに、二項演算子gを適用。
- (f g) は2つの演算子のフック(説明略)を作る。
- (f g h) は3つの演算子のフォーク(説明略)を作る。
極悪非道な文法ですな。