湯婆婆はお年を召していそうです。ということはその実装も相応の年の言語を用いるのが筋ではないでしょうか。
ということで僕が触れる中で一番古い言語のAPLを使ってみました。
APLの見た目はこんな感じです。
R ← 16
(~R∊R∘.×R)/R←1↓⍳R
2 3 5 7 11 13
1⌽,⍨9⍴'''1⌽,⍨9⍴'''
ググってたら APL (Alexa presentation language) というのがありましたが、それではありません。最新ガジェットのハックを期待された方はすみません。
FORTRAN?僕が知っているのはFortranです。あとアセンブリとLISPはちょっと・・・。
元ネタ
環境
Windows上でNARS2000を使いました。
ここからダウンロードできます:
http://www.nars2000.org/download/Download.html
個人的に、最新版が2020年5月にリリースというのがびっくりでした。いまだにAPL開発する物好きがいるんだなあ、と
表記について
NARS2000では入力で行頭にインデントがなされていて、出力や標準入力はインデントがありません。それに倣い、以下の例ではインデントしたところはプログラム本体、インデントが無いものは出力や標準入力を表すものとします。
(式、変数)
出力
(標準入力を要求する式)
標準入力
NARS2000の対話ワークスペースでの見た目を再現しようとしています。
文法
実装にあたり必要な文法をここに書きましたが、少々長く、またAPLを既知の人には不要な文法のいろはですので、畳んでおきます。
ここをクリックして開閉
代入
←
を使います。これは直感的ではないでしょうか。
s ← "hoge"
x ← 1..6
n..m
は±1ごとに n
からm
まで並べます。
1..3
1 2 3
4..2
4 3 2
x
は 1, 2, 3, 4, 5, 6という配列になります。以下、s
と x
をこの定義で使います。
配列の長さ
⍴
により配列の長さを取得できます。正しくは行列のshapeオペレーター。
⍴x
6
⍴s
4
要素アクセス
配列、文字列ともに []
です。
x[2]
2
s[3]
g
Fortranやjuliaと同じ1オリジンですね。
今回は使いませんが、行列の時は ;
でRankを分けます。
m ← 3 3 ⍴ 1..9
m
1 2 3
4 5 6
7 8 9
m[1;2]
2
m[;3]
3 6 9
先ほどは配列長さとして紹介した⍴
ですが、この例では異なる役割です。APLでは右側だけに引数をとるmonadic (ρxみたいなの) と、両側に引数を置くdyadic (n/mみたいなの)があります。また、同じ記号でもmonadicとdyadicで役割が異なります。⍴x
はmonadicでは配列x
の形状を返し、d⍴x
では x
を形状 d
にreshapeしたものを返します。
文字列の結合
普通に ,
を並べるとなんでも結合できます。
"hoge", "fuga"
hogefuga
"Array x is [", x, "]"
Array x is [ 1 2 3 4 5 6 ]
注意として、APLでは優先順位関係なく演算子は右から順に作用します。
"hoge", ⍴s, "fuga"
hoge 8
この例ではまず一番右の ,
が処理されて
"hoge", ⍴"hogefuga"
が評価されています。
乱数
?
を使います。 ?n
で $[1, n]$ の一様?乱数を返します。
?10
6
?4/10
9 1 9 2
ここで、L/R
はR
をL
個並べた配列を返します。4/10
は 10 10 10 10
です。
?0
とすると $(0, 1)$ の浮動小数点の乱数を返します。負の値や少数を与えるとDOMAIN ERROR
となるようです。
改行と出力
⎕
に何か代入すると、その何かを改行付きで出力し、そのまま返します。
y ← ⎕ ← x
1 2 3 4 5 6
これで y
に x
の内容がそのまま代入されつつ、同時に出力もされています。APLでは改行文字を出すのが面倒だったので、これを使います。
標準入力
キーボードから代入する ⍞
を使います。
s ← ⍞
hoge
2行目のhoge
はキーボードで入力し、Enterを押したものです。これで s
に"hoge"
が入ります。
s
hoge
関数定義
今回はmonadicのみを使います。
Anonymous function/operator
python の lambda
みたいなのは {}
で。
square ← {⍵*2}
square 4
16
ここで ⍵
は仮引数です。
Open function/operator
∇
を使う定義もあります。
∇ res ← (square) x
これを実行するとNARS2000では別ウィンドウが開き、そこで定義ができます。
res ← (square2) x
res ← x*2
こういう感じで入力が終わればCtrl-sしてやれば、元のWorkspaceに定義が反映されます。
処理分割
普段のコード本文では何行でも処理を書けるのですが、無名関数内など1行しか書けないことがあります。そんな時、複数の処理をつなげる演算子として ⋄
があります。Cやphpにおける ;
のようなものです。
x ⋄ s
1 2 3 4 5 6
hoge
NARS2000での面倒くささ
日本語入力
日本語は入力できないだけで、マルチバイト文字も対応しているようです。使う場合は他のアプリから文字列をコピペすればよいです。
キーバインド
日本語キーボード、日本語入力だろうとUSキーボードとして認識されています。例えば日本語キーボードの人は *
はShift押しながら8を、{}
は@[を押さないといけません。
APL湯婆婆の本質
{⍵[?⍴⍵]}⍞
湯婆婆
無名関数で
以下の3つの関数を1行ずつ順番に定義します。無名関数内で変数の代入ができないようなので、入れ子にして引数という形で取り出した名前を確保しました。
yubaba_declare ← {"今からお前の名前は", ⍵, "だ。いいかい、", ⍵, "だよ。分かったら返事をするんだ、",⍵,"!!"}
yubaba_rename ← {⎕←"フン。", ⍵, "というのかい。贅沢な名だねぇ。"⋄yubaba_declare ⍵[?⍴⍵]}
yubaba_keiyaku ← {⎕←"契約書だよ。そこに名前を書きな。"⋄yubaba_rename ⍞}
使用例
yubaba_keiyaku
契約書だよ。そこに名前を書きな。
ボボボーボ・ボーボボ
フン。ボボボーボ・ボーボボというのかい。贅沢な名だねぇ。
今からお前の名前はボだ。いいかい、ボだよ。分かったら返事をするんだ、ボ!!
文字列が評価される式を⋄
でつないだのに⎕
を使わないとそもそも出力されなかったのが何故か分かっていない。
なんで最後の関数のyubaba_declare
で⎕
が要らないかがよくわかっていない。
何とかいってAPLを真面目に使ったことないので、ちょっと込み入ったことは分からないのです。すみません。
Open Functionで
引数のないサブルーチンを定義してみます。
∇ res← (Yubaba)
実行して出てきた新しいウィンドウで以下のように入力します。
res← (Yubaba)
⎕ ← "契約書だよ。そこに名前を書きな。"
name ←⍞
⎕ ← "フン。", name, "というのかい。贅沢な名だねぇ。"
short← name[?⍴name]
⎕ ← "今からお前の名前は", short, "だ。いいかい、", short, "だよ。分かったら返事をするんだ、",short,"!!"
res ← short
終わったら Ctrl-sして定義を反映させます。
使用例
x ← Yubaba
契約書だよ。そこに名前を書きな。
Fortran
フン。Fortranというのかい。贅沢な名だねぇ。
今からお前の名前はFだ。いいかい、Fだよ。分かったら返事をするんだ、F!!
x
には短縮された名前(この例ではF
)が入っています。
境界値
Yubaba
契約書だよ。そこに名前を書きな。
フン。というのかい。贅沢な名だねぇ。
DOMAIN ERROR
Yubaba[4] short← name[?⍴name]
∧
空文字列に対しては DOMAIN ERROR
になります。これは ?0
が $(0, 1)$ の浮動小数点の乱数を返すようになり、結果配列のインデックスに小数が指定されたことによるエラーです。
湯婆婆オペレーター
∇ res ← (Yubaba2) name
res ← name[?⍴name]
使い方
Yubaba2 "hoge"
h
Yubaba2 "hoge" "fuga"
hoge
Yubaba2 1..6
5
ただの配列の一番上の次元からランダムに要素を一つ取り出すだけのオペレーターです。
まとめ
ゴルフしてそうな湯婆婆ですね。