NARS2000でAPLを体験してみた

これは「[ドワンゴ Advent Calendar 2017](https://qiita.com/advent-calendar/2017/dwango)」6日目の記事です。

# APLとは

APL (A Programming Language または Array Processing Lanauge の略) は古い ([Wikipediaによると1964年](https://ja.wikipedia.org/wiki/APL)) プログラミング言語です。APL で書かれたコードを見たときになによりも目を引くのは ASCII にはない独特な記号でプログラムが記述されているところです。具体例としては以下のページが参考になります。

- [APLのイディオムを集めたページ](https://aplwiki.com/FinnAplIdiomLibrary)

かつては商用の処理系もいくつもあり、いろいろな場面で使われていたようです。現代ではかつてほどの勢いは失われてしまったものの特にヨーロッパでは利用している人たちがそれなりにいるようです。以下はAPL処理系の一つであるDyalogの会議のプログラムです。

- [Dyalog '17](https://www.dyalog.com/user-meetings/dyalog17.htm)

私はAPLという言語の名前と特殊な記号を使うということを知識としては知っていたのですが、実際に触ったことはこれまでありませんでした。人生で一度ぐらいやってみるのも面白そうだと思ったので、今回はAPLを体験してみます。

# APL処理系

2017年時点で無料で利用可能なAPL処理系を探したところ、以下のものが見つかりました。

- [Dyalog APL](https://www.dyalog.com/) (非商用であれば無料, Windows, Mac OS, Linux, [Raspberry Pi](https://github.com/APLPi))
- [NARS2000](http://www.nars2000.org/) (OSS, Windows)
- [GNU APL](https://www.gnu.org/software/apl/) (OSS, Linux, Windows)

今回はNARS2000を利用します。NARS2000は、

- ダウンロードとインストールが簡単
- 専用のIMEを頑張ってインストールする必要がない
- 簡易IDEが付属している

という特徴があり、試しに使ってみるには一番都合が良かったので選びました。

# 実行してみる

NARS2000を起動すると以下のような画面が表示されます。スタートアップメッセージは設定で非表示にしました。

![image01.png](https://qiita-image-store.s3.amazonaws.com/0/70306/bc01563d-487e-40ab-5dbf-3d35007cfd3c.png)

APLは対話的に式を処理するシステムとして設計されています。インデントされた位置にあるカーソルに式を入力してEnterを押すと結果が表示されます。

```
      12
12
      2 + 3
5
      2 × 3
6
      X ← 12
      X × 3
36
```

かけ算の記号に `*` ではなく `×` を使うのが多くの言語とは異なっています。この `×` は画面上部のLangツールバーのボタンをクリックするか、キーボードで `Alt-'-'` を押すと入力できます。代入は `←` (`Alt-'['`) を使います。

# 配列の演算

APLの特徴のひとつである配列は、`10 20 30` のように値を空白で区切って指定します。

``` 
      10 20 30 + 4 5 6
14 25 36 
      10 20 30 × 2
20 40 60 
```

演算子の両辺に配列を指定したり、片方をスカラ値にしたりできます。

APLで得点の平均値を求めてみます。

```
      Scores ← 30 60 80 10 50
      Sum ← ⎕ ← +/ Scores
230
      Length ← ⎕ ← ⍴Scores
5 
      Sum ÷ Length
46 
```

`+/` を使うとリストの合計を計算できます。`+` に限らず任意の演算に `/` をつけることができます。`⎕ ← 式` を実行すると式の中間結果を表示できます。`⍴` は配列の長さです。ちなみにオペレータの使い方はLangツールバーのヒントや以下のWikiを見て調べます。

- [NARS2000 Wiki](http://wiki.nars2000.org/)

平均の計算を関数にするには次のようにします。

```
      Average ← {(+/⍵)÷(⍴⍵)}
      Average Scores
46 
```

`{...}` で無名関数を作ります。引数(右辺)は `⍵` で参照します。

ついでに標準偏差です。

```
      Stdevp ← {√(+/(⍵-(+/⍵)÷⍴⍵)*2)÷⍴⍵}
      Stdevp Scores
24.16609195 
```

# システム関数

ファイル入出力などの演算子が定義されていない操作にはシステム関数を使うようです。システム関数は `⎕` から始まる名前でアクセスできます。ファイルを開くには `⎕ntie`、読み込みに `⎕nread`、閉じるのに `⎕nuntie` を使います。

```
      file ← 'c:\work\test.txt' ⎕ntie 0
      str ← ⎕nread file
      ⎕nuntie file
      str
hello, world.
```

ファイルを作って書き込むには `⎕ncreate` と `⎕nuntie` を使います。

```
      file ← 'c:\work\out.txt' ⎕ncreate 0
      'hello, world.' ⎕nappend file
      ⎕nuntie file
```

このあたりの情報は以下のページに書かれています。

- [System Function Native Files - NARS2000](http://wiki.nars2000.org/index.php?title=System_Function_Native_Files)

ファイルの作り方が分かったので、ビットマップ画像を関数にまとめます。エディタ上で以下のように入力するとユーザ関数を作る画面が開きます。`)` で始まるコマンドはシステムコマンドと呼ばれています。

```
)edit genimage
```

新しいウインドウに定義を入力していきます。今回作るユーザ関数 `genimage` は中置記法でファイル名とビットマップのデータを受け取ります。

```
filename genimage data
```

バイナリで開くために、インタプリタ上の符号付きの64bit数のうち符号なしの8bitで表せる範囲をファイルに書き出します。以下はそのような設定です。

```
file ← filename ⎕ncreate 0 1 ('char8' 'int64')
```

今回は縦64px、横64px、1ピクセルあたり8bitの画像とします。まだまだコーディングに慣れていないので設定はコードに埋め込みました。

```
⍝ header
66 77 52 20 0 0 0 0 0 0 52 4 0 0 ⎕nappend file
40 0 0 0 64 0 0 0 64 0 0 0 1 0 8 0 ⎕nappend file
0 0 0 0 0 16 0 0 72 0 0 0 72 0 0 0 ⎕nappend file
0 1 0 0 0 1 0 0 ⎕nappend file
```

`⍝` (Lamp) はコメントの開始記号です。

カラーパレットを作ります。今回は0を黒、255を白としたグレースケールを登録します。一つの色はRGBの値とダミーの合計4バイトで表現します。作り方はまずは `⍴` を使って同じ値を3回繰り返し、`↑` で長さを4にして足りない部分に0を埋めています。

```
⍝ palette
color ← {4↑⊃3⍴⍵}
(1024⍴⊃(color¨0..255)) ⎕nappend file
```

最後にビットマップデータを書き込めば完成します。

```
⍝ bitmap
(4096 ⍴ data) ⎕nappend file
⎕nuntie file
```

保存して元の画面に戻ると関数が登録されています。登録されている関数は以下のように確認できます。

```
     )fns
color    genimage
```

うっかり補助関数 `color` が外にはみ出してしまっています。本来はローカルに閉じておくべきですが気にせず進みます。

関数 `genimage` は以下のように利用できます。

```
'c:\work\out1.bmp' genimage 0 
```

これで真っ黒なビットマップになります。

![out1.png](https://qiita-image-store.s3.amazonaws.com/0/70306/725215e1-d447-5a7d-39f8-9d195990cd32.png)

```
'c:\work\out2.bmp' genimage 127
```

値を大きくすると色が明るくなります。

![out2.png](https://qiita-image-store.s3.amazonaws.com/0/70306/bfea4845-e58d-b9d7-abc8-48863d694a20.png)

# 画像を作って遊ぶ

画像が出力できると結果を目視で確認できるので便利ですね。次は模様を作ってみます。

0を8回、127を8回を繰り返すとしましまができます。

```
      8 ⍴ 0 1
0 1 0 1 0 1 0 1
      8 ⍴¨ 0 1
 0 0 0 0 0 0 0 0  1 1 1 1 1 1 1 1
      ⊃8⍴¨0 1
0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1
      'c:\work\out3.bmp' genimage (⊃8⍴¨0 127)
```

![out3.png](https://qiita-image-store.s3.amazonaws.com/0/70306/3af3ac19-c4a3-be94-a7ba-016c2f7f10fa.png)

さらに工夫するとチェッカー模様になります。

```
      pat1 ← 512⍴⊃8⍴¨0 127
      'c:\work\out4.bmp' genimage (pat1⍪⊖pat1)
```

![out4.png](https://qiita-image-store.s3.amazonaws.com/0/70306/1d1fa53e-fd4e-0cae-f3aa-ab1732918342.png)


APLのouter product演算を使うとかけ算の九九の表は次のように書けます。

```
      X ← 1..9
      X∘.×X
1  2  3  4  5  6  7  8  9
2  4  6  8 10 12 14 16 18
3  6  9 12 15 18 21 24 27
4  8 12 16 20 24 28 32 36
5 10 15 20 25 30 35 40 45
6 12 18 24 30 36 42 48 54
7 14 21 28 35 42 49 56 63
8 16 24 32 40 48 56 64 72
9 18 27 36 45 54 63 72 81
```

これをつかって半径25ピクセルの円を描いてみます。座標毎に円の中に入っているかどうかを判定する関数を作りouter productで画像を生成しています。

```
      circle ← {(√+/((⍺ ⍵)-32)*2)≤25}
      pat2 ← (X ∘.circle X) × 127
      'c:\work\out5.bmp' genimage pat2
```

![out5.png](https://qiita-image-store.s3.amazonaws.com/0/70306/e1e367d3-9b76-d876-4fa2-424dff644835.png)

がんばればシェーダの要領で好きな画像を表示できそうです。

# APLを体験した感想

APLは見たことのない記号がたくさん出てくる言語のため、最初はとっつきにくさもあるのですが、キーボードショートカットも含め触っているとすぐに慣れてしまうということが分かりました。中学生時代、単語の意味を理解しないままコードを書いていた時代を思い出しました。

APLという言語は、まだまだ始めたばかりであまり全貌をつかめてはいないのですが、ちょっとコードを書くだけでも、日頃使っているプログラミング言語とは発想の仕方が異なっていて面白いです。ちょうど関数型言語を始めた頃、何でもポイントフリーで書きたくなった時のようなわくわく感があります。

おそらく業務でAPLを書くようなことは無いと思いますが、日頃自分が触っていたり耳にしたりする言語・環境・ツールがこの世の中のすべてではないということを、肝に銘じてコードを書いていこうと、思いを新たにしました。