Rubyist のための他言語探訪 【第 12 回】 APL と J

著者:まつもとゆきひろ

APL と J

今回は古典的言語の一つ、APL とその後継である J について紹介します。

APL

APL は「A Programming Language」の略称です。 なんとも人を食ったような名前ですね。 名前からは典型的なプログラミング言語を連想するのですが、実際には空前絶後、隠れた愛好者は多いものの、絶対に主流にはならないことが運命付けられた言語でもあります。

APL は 1957 年 Kenneth E. Iverson によって発明された記法で、1962 年に発行された彼の著書『‘A Programming Language’』で発表され、1964 年に実際の処理系が開発されています。

APL の利点は、配列操作機能が非常に強力な点です。 これにより APL によるプログラミングは (当時主流だった FORTRAN などと比較して) 非常に生産性が高かったと伝えられています。 しかし、それよりなにより最大の特徴は、プログラミングに特殊な文字列を必要とする点です。 この結果 APL のプログラムは通常の端末では表示することさえできず、プログラマは APL プログラムを作成する時には、タイプライターのボールを取り替えていた1ということです。

この結果、APL は非常に密度が高く、簡潔なプログラムを書くことができたということです。 Paul Graham の言う通り、プログラミング言語においては「簡潔さは力なり」ですから、つまり、APL は非常にパワフルな言語であると言えます。 配列の各要素に対して一度にいろいろな演算子を適用できるのは Ruby の Enumerable を使う快適さに近いものがあります。 しかも、より APL の方がより強力です。 聞くところによると会計や統計処理の分野では並ぶもののない生産性を実現し、ファンも多かったということです。 しかし、同時にこの特殊文字による簡潔さは APL プログラムの読解を困難なものとして、APL のいわば「元祖 write-only 言語」としての地位を不動のものとしました。

正直、私も APL プログラムは読めません。 図 1 に APL プログラムを (画像で) 示します。 これが何をするプログラムなのか聞かないでくださいね2

  • 図 1 APL サンプルプログラム (Wikipedia より引用)
    LifeInApl.gif

APL で使う記号を図 2 に示します。

  • 図 2 APL 記号
    legwork-12-2.png

なんと恐るべきことにこれらの記号はすべて Unicode に含まれています。 ということは、現代の Unicode 端末では APL プログラムを表示することができるということです。 もしかすると、APL 復権の日は近いのかもしれません。

なお、Iverson は APL を発明した業績により 1979 年の Turing 賞を受賞しています。

J

Unicode に APL 文字が導入されたのは、APL の歴史から見るとつい最近のことです。 それまでの間、APL は特殊文字という事情による不遇を耐えてきました。 たとえば、ASCII 端末では Σ を SIGMA と書くなど工夫が行われましたが、それでは APL の簡潔さによるパワーを失ってしまいます。

そこで、APL のパワーを実現しつつ、ASCII の範囲内でプログラムできる後継言語が待ち望まれました。 APL の後継としては A+、K などさまざまな言語が登場しましたが、今回ここで紹介する J は、開発に Iverson 自らがかかわっているなど、正統後継者の雰囲気が高いものです。

J は 1990 年代はじめごろに Iverson と Roger Hui によって開発されました。 J は APL の配列指向プログラミングと FP の関数型プログラミングの両方を支援しています。

J の文法的特徴

J は APL から受け継いだいくつかの文法的特徴があります。

負の数に単項マイナスを使わない

J では負の数値に単項マイナスを使いません。 -3 は「_3」と表現します。 また、「_」単体だと無限大 (infinity) を意味します。

演算子の優先順位がない

J の演算子には優先順位がありません。 すべて右優先で結合します。 ですから、

1 * 2 + 3

は、常に

1 * (2 + 3)

と解釈されます。

同じ演算子が二項と単項で意味が違う

たとえば「+」演算子は二項では加算ですが、単項では共役複素数になります。

インタラクティブセッション

J の基本はインタラクティブセッションです。 つまり、一行入力しては結果を得るというのが基本的プログラミングスタイルになります。 ですから、J における「Hello World」はこのようになります。

'Hello World'

文字列を入力すると、J はそのままエコーしますから、これで一応「Hello World」が成立します。

サンプルで学ぶ J

J の全容は短い記事ではとても紹介できそうにありません。 ここでは、サンプルプログラムとそのコメントを通じて、J によるプログラミングの雰囲気を掴んでもらいましょう。

まず、コメントですが「NB.」から改行までがコメントです。 正気とは思えません。 四則演算は割と普通ですが、割り算がスラッシュではない点に注意してください。

  2+3  NB. 足し算
5
  2-3  NB. 引き算
_1
  2*3  NB. 掛け算
6
  2%3  NB. 割り算 (「/」ではない)
0.67

すでに述べたように単項演算子 (Monads) と二項演算子 (Dyads) はそれぞれ異なる意味を持ちます。

  -4   NB. 符号反転
_4
  *4   NB. 符号 (正なら 1、負なら _1、0 なら 0)
1
  %4   NB. 逆数
0.25

J の特徴といえば配列演算です。

  1 2 3 + 4 5 6  NB. 要素同士の加算になる
5 7 9
  1 + 3 4 5      NB. 要素数を揃えてから加算する
4 5 6
  1 2 3 4 > 2    NB. 真の時 1、偽の時 0
0 0 1 1
  x =: 1 2 3 4   NB. =: で代入する

  - x            NB. 各要素の符号反転
_1 _2 _3 _4
  + / x		 NB. 要素同士を加える (/ は演算子の挿入)
10
  + / x > 2	 NB. 2 より大きい要素がいくつあるか
2
  1 2 = 1 2      NB. 要素同士の比較
1 1
  + / x = x	 NB. 要素の数 (宿題: なぜそうなるか)
4
  # x		 NB. 配列の長さを求める演算子
4

配列に単項演算子を適用すると、各要素に対して単項演算子を適用した配列が得られます (map/collect 相当)。 二項演算子を「/」と一緒に適用すると各要素間に演算子を挿入した結果を返します (reduce/inject 相当)。

高階関数

J では関数そのものも値として取り扱うことができます。

  square =: *:   NB. alias。単項「*:」は二乗

  square 1 2 3 4
1 4 9 16  
  sum =: + /     NB. 複数の演算子を組み合わせられる

  sum 1 2 3
6
  double =: * & 2 NB. カリー化のようなことも

  double 6
12

まとめ

J にはここまででは説明できないとてもたくさんの演算子がありますし、多次元配列などのデータ型もサポートしています。 Google の Sawzall などでも注目されている map/reduce の考え方も取り込まれており、とても強力な言語だと思います。

しかし、正直に言わせてもらえば、J はとても読みにくい。 慣れればなんとかなるのでしょうか。 APL を直接継承するよりも、むしろ、Ruby における Enumerable のようなものを強化するとか、NArray のような配列型クラスを提供するとかによって、もっと「普通の言語」で APL のコンセプトだけを提供した方が良かったのではないかと思わないでもないです。 「わざわざ紹介しといてそれかよ」というような結論ですが。

J についての情報は以下のサイトから入手できます。

Jsoftware
J 言語メインサイト
J Wiki
J 言語に関する Wiki

著者について

matz.jpgまつもとゆきひろは自他ともに認める日本を代表する言語オタクです。 言語好きが昂じて自分の言語を設計してしまった大馬鹿者です。 が、オタクとかハッカーとか呼ばれる人種はみんな多かれ少なかれそんなものじゃないでしょうか。

バックナンバー


タイプライターの印字は表面に文字が刻まれたボールで紙をインクリボンの上から叩くことで行われており、フォントの変更はそのボールを取り替えることで実現されていた。

http://computermuseum.informatik.uni-stuttgart.de/dev/ibm_735/kk_courier_10_044.jpg (シュトゥットガルト大学サイトより引用)

  1. ボールを取り替えていた: 当時のコンピュータの入出力は一種の電動タイプライターにより行われていた。 

  2. 聞かないでくださいね: 図 1 のプログラムはマトリクスを受けて Conway のライフゲームの次世代を計算するプログラムです。