APL/J言語:ベース変換

Easy Jの第6章は「可能性を調べる」という表題で、まず課題が述べられます。
「仮に、ふた通りの選択がありうる状況とする。それぞれについて、なにかが受け入れられるか拒絶されなければならない。たとえばオニオンをいれる(またはオニオン抜き)、チーズ、ソーセージ、あるいはマッシュルームをピザに入れるかどうかというのでもいい。目的はすべての可能性についての料金表を作ることである。

課題に入る前に動詞を二つ導入する。#.(シャープドット、ベース変換)、と#:(シャープコロン、アンチベース)である。これらはどんな単位のものでも変換してしまう。4時間22分54秒を秒に変換するとか、2ヤード2フィート9インチをインチに変換するのは次のようにやる。

   0 60 60 #. 4 22 54    NB.分と秒のベースは60と60である。
15774
   0 3 12 #. 2 2 9   NB.ヤードとフィートとインチにする
105

うむ。検算してみよう。

   (4*60*60)+(60*22)+54
15774
   (2*3*12)+(2*12)+9
105

合っているみたいですね。しかしカッコがばかみたいなので、書き換えよう。

   54+60*22+4*60
15774
   9+12*2+3*2
105

ふむふむ。1ヤードは3フィートなのですね。で、1フィートは12インチ。
次に、この逆をやるのがアンチベースです。#:(シャープコロン)。

   0 60 60 #: 15774 NB.秒を時間、分、秒にする
4 22 54
   0 3 12 #: 105 NB.インチをヤード、フィート、インチにする
2 2 9

右側の引数がリストだと結果はリストのリストになる。

   10 10 10 10 10#:4342 8958 4646 243 10 12334
0 4 3 4 2
0 8 9 5 8
0 4 6 4 6
0 0 2 4 3
0 0 0 1 0
1 2 3 3 4

左側の引数が無い場合はベースを2として算出される。バイナリー(2進法)への変換結果が得られる。

   #:i.8 NB.8=*/2 2 2なので、#:i.*/2 2 2とも書ける
0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1

トランスポーズ|:(たてぼうコロン、たてよこ変換、トランスポーズ)を使うと1列目が1行目になるように縦と横が変換され、見やすくなる場合がある。

   |:#:i.8
0 0 0 0 1 1 1 1
0 0 1 1 0 0 1 1
0 1 0 1 0 1 0 1

さて、ピザの問題に戻って、オニオン、チーズ、ソーセージ、マッシュルームのトッピングの可能性は次のようになる。ゼロは選ばなかった場合で1がトッピングにのせた場合である。

   |:#:i.16
0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1
0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1

アンチベース(#:、シャープコロン)の左側は全部同じである必要はなくて、たとえばチーズは2種類あるとして、選択肢としてはなし(0)も含めて3つあるとすると、全部の選択肢は16から24に増加する。左側引数は2 3 2 2となる。

   |: 2 3 2 2 #: i.24
0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1
0 0 0 0 1 1 1 1 2 2 2 2 0 0 0 0 1 1 1 1 2 2 2 2
0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1

考えてみれば24は2と3と2と2の掛け算の結果(product、プロダクト)である。J言語でいえば*/2 3 2 2である。ということは上記の式(エクスプレッション、expression)には冗長性があるということであり、動詞を連結するときのルールのフック(hook)を使うと24と2 3 2 2の両方を入れる必要がなくなる。つまり、possという動詞を作り、左側動詞を#:(シャープコロン)、右側動詞をi.@(*/)(アイドットアトマーク左かっこアスタリスクスラッシュ右かっこ閉じ)とすればよい。

   poss=:#:i.@(*/)
   |: poss 2 3 2 2
0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1
0 0 0 0 1 1 1 1 2 2 2 2 0 0 0 0 1 1 1 1 2 2 2 2
0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1

さて、>(小なり記号、オープン)と;(セミコロン、リンク、ボックス化)を使って、トッピングのリストをつくろう。

   ]tops=:>'onions';'cheese';'sausage';'mushrooms '
onions    
cheese    
sausage   
mushrooms

オニオンとマッシュルームが複数形でチーズとソーセージに's'が付いていないのが不思議ですが、英語の話ではないのでそれは飛ばして、](右ブラケット、右角かっこ)は右側の引数の内容を表示する動詞です。ここの場合だと+(プラス)でも,[(左ブラケット、左角かっこ)でも同じ結果になりますが、変数を作成すると同時に内容を示すにはこの](右ブラケット、右角かっこ)が通常使われます。+(プラス)だと文字列に対応しません。[(左ブラケット、左角かっこ)は意味あいが違う。
次にこの文字列と先ほどの4x24のテーブルをひとつの表にするために、4x24のテーブルをそのままtabという変数に入れます。

   ]tab=:|:poss 2 3 2 2
0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1
0 0 0 0 1 1 1 1 2 2 2 2 0 0 0 0 1 1 1 1 2 2 2 2
0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1

topsが文字列で、がtabが数字のテーブルですので、そのままで,(カンマ)でつなげたのではエラーになってしまいます。ボックスにする手もありますが、数字を文字列にするフォーマットと呼ばれる動詞があります。":(クオートコロン、フォーマット)、出力の見た目が同じですが、文字列になります。":(クオートコロン、フォーマット)は二項動詞として使うと左側の数字がコラムの幅(フィールド幅)になります。あと、,.(カンマピリオド)を使うことで、表を横につなげます。通常の,(カンマ)では縦につなげることになってしまう。

   tops,.1":tab
onions    000000000000111111111111
cheese    000011112222000011112222
sausage   001100110011001100110011
mushrooms 010101010101010101010101

ここから先、行列の積(+/ . *)(プラススラッシュスペースドットスペースアスタリスク)もからんでよくわからない話になりますので、ざっと読み飛ばして下さい。

   ]cost=:0.80 1.80 2.20 1.50  NB.それぞれのトッピングの価格
0.8 1.8 2.2 1.5
  2 12 $ cost +/ . * tab NB.24通りの場合のトッピング価格合計
  0 1.5 2.2 3.7 1.8 3.3   4 5.5 3.6 5.1 5.8 7.3
0.8 2.3   3 4.5 2.6 4.1 4.8 6.3 4.4 5.9 6.6 8.1

一覧表にしていきます。まず、":(フォーマット、クオートコロン)を使って、最初の4文字を一桁ずつ小数点以下なし、と、6桁ずつ小数点以下2桁を含む形で出すと以下のようになります。

   ((4#1j0), 6j2) ":|:tab, cost +/ .*tab
0000  0.00
0001  1.50
0010  2.20
0011  3.70
0100  1.80
0101  3.30
0110  4.00
0111  5.50
0200  3.60
0201  5.10
0210  5.80
0211  7.30
1000  0.80
1001  2.30
1010  3.00
1011  4.50
1100  2.60
1101  4.10
1110  4.80
1111  6.30
1200  4.40
1201  5.90
1210  6.60
1211  8.10

オニオン、チーズ、ソーセージ、マッシュルームというのを数字の位置ではわかりにくいので、OCSMというような表示にする。

   $prices=:cost+/ . * tab
24
   $,.prices
24 1
   ((|:tab)#'OCSM'),.6j2":,.prices
       0.00
M      1.50
S      2.20
SM     3.70
C      1.80
CM     3.30
CS     4.00
CSM    5.50
CC     3.60
CCM    5.10
CCS    5.80
CCSM   7.30
O      0.80
OM     2.30
OS     3.00
OSM    4.50
OC     2.60
OCM    4.10
OCS    4.80
OCSM   6.30
OCC    4.40
OCCM   5.90
OCCS   6.60
OCCSM  8.10

以上