dcコマンドで遊んでみた
dc
コマンドってのがあります。lsを打ち間違えてやってしまうslコマンド的な・・・ってわけではなくて、れっきとした便利ツール。
逆ポーランド記法の計算機で、精度設定できたりマクロ使えたりします。
と言うわけでこのマクロとやらを試してみた。 ほぼお遊びなので、あんまり参考にはならないと思います。
とりあえず実行
$ dc
とすればインタプリタが起動します。インタプリタってもプロンプトとかなんにも表示されないけれど。
終了する時はq
とタイプすればおっけーです。
簡単な計算をするときなんかは
$ dc -e '1 2 +p'
とかやって実行も可能。1+2の結果を表示します。
他にも、
$ dc filename
ってやってファイルの中身を実行するとかも出来ます。
$ dc file1 file2
みたいにすれば複数のファイルを実行する事も可能。先頭から順番に実行されるようです。
基本的な計算
とりあえず適当に数字を入力してスタックに積みます。
1
2
こんなん。改行区切りでもいいし、スペース区切りとかでも良い。
んで、スタックの中身を足したり引いたり。
+
例えばこれは足し算。四則演算の記号は+-*/%%^みたいなよくあるやつです。
計算した結果は
p
とすれば表示されます。
スタックの中身を全部みたいときは
f
で表示。上の方がスタックの先頭側です。
レジスタを使う
本題のマクロに入る前にレジスタについて。vimのレジスタとかなり似てる、かも。
sa
みたいにすると、スタックの先頭をa
レジスタに移動しします。
レジスタの名前は文字とか数字とか記号とか諸々らしい。sb
とかsc
とかやって使います。
a
レジスタの中身は
la
とすれば取り出せます。同様にlb
とかlc
とかやれば他のレジスタからも出せる。
何度取り出しても中身は消えない。
マクロを使ってみる
んで本題、マクロ。
[1+]
みたいな感じでマクロを作れます。作ったマクロはスタックの先頭に入ります。
マクロはx
コマンドで実行できます。
流れとしてはこんな感じ
2 # スタックに2を積む。
[1+] # 1を足す動作をマクロとしてスタックに積む。
x # 実行する。
p # 結果を確認する。
3 # 計算結果。
スタックに2を入れて、そんでもって先頭を1増やすマクロを作って、実行する。
まとめて書くと2[1+]xp
って感じ。2 1+p
で済むのに、長い。
x
で実行するとスタックから消えてしまうので、実際はレジスタに入れて実行することになります。
[1+]sa # aレジスタに1増やすマクロを入れる。
2 # 2をスタックに積む。
la # aレジスタからマクロを取り出して、
x # 実行する。
lax # もっかい実行してみる。
p # 結果を確認してみる。
4 # これは計算結果。
こんな感じで。 レジスタに入れれば何度でも実行できる。
合計値を計算してみる
ちょっと高度な事をしてみよう。と言うわけで、スタックに積んだ値の合計を計算してみます。
とりあえず結論から。
[+z1<a]sa # マクロ作る
1 2 3 4 5 # 数字積む
lax # 実行
p
15 # 結果
こんな感じ。ややっこい。
マクロを実行すると、とりあえず+
でスタックの先頭二つを取り出して足します。
んでもってスタックの長さをz
でスタックに積んで、その後に1
を積む。
<
で比較をして、スタックの先頭の方が小さければa
レジスタの内容を実行。
みたいな流れで計算します。大分ややっこいね。
ポイントは比較してマクロを実行できる<
でしょうか。
他にも!<
とか>
とか!>
とか=
とか!=
とか。めっちゃあるので調べてください。
なんというか、アセンブリ言語のjg
とかjl
とかににている、かも?
マクロの内容を擬似コードっぽく表すと
function a(){
push(pop() + pop());
if(length() > 1){
a();
}
}
push(1);
push(2);
push(3);
push(4);
push(5);
a();
print(pop());
的な感じ? 何だこの難解言語。
階乗を計算してみる
せっかくなので階乗も書いてみよう、と言うわけで。
[d1-d2<a*]sa
5
lax
p
こんなん。何かもうbrainfu*kみたいになってる。
今度のマクロは実行されたらd
を使ってスタックの先頭をコピーします。
コピーした値を1-
でデクリメントします。
d
でもっかい値をコピーして、2
と<
で比較。2以上なら再帰的に自分を呼び出す。
最後にレジスタの先頭二つを*
でかけて終了。
d
でスタックの先頭をコピーできるの、結構便利。なのかも。
これも擬似コードで表すと、
function a(){
copy();
push(pop() - 1);
copy();
if(pop() > 2){
a();
}
push(pop() * pop());
}
push(5);
print(pop());
大体こんなん。 分かり安くなった気がしない…。
スタックの中身はこんな感じで推移する、はず。
5 la x
5 [d 1 - d 2 <a *] x
5 d 1 - d 2 <a *
5 5 1 - d 2 <a *
5 4 d 2 <a *
5 4 4 2 <a *
5 4 d 1 - d 2 <a * *
5 4 4 1 - d 2 <a * *
5 4 3 d 2 <a * *
5 4 3 3 2 <a * *
5 4 3 d 1 - d 2 <a * * *
5 4 3 3 1 - d 2 <a * * *
5 4 3 2 d 2 <a * * *
5 4 3 2 2 2 <a * * *
5 4 3 2 d 1 - d 2 <a * * * *
5 4 3 2 2 1 - d 2 <a * * * *
5 4 3 2 d 2 <a * * * *
5 4 3 2 2 2 <a * * * *
5 4 3 2 * * * *
5 4 6 * * *
5 24 * *
120 *
うーん…。長い。
最後に*
が残ってしまうのだけれど、これなんやろね。
平均
さっき作った合計値を利用して
[+z1<s]ss
[zsx lsx lx /]sa
1 2 3 4 5
lax
p
みたいな感じで平均を計算できる。
小数点以下の桁数は2k
とか3k
とかやって設定できます。
最大/最小
最大値は
[sx]sc # スタックの値をレジスタに移動するマクロ
[n]sd # スタックの頭を消すマクロ
[dlx<c dlx>d z1<m]sm
0sx
1 3 2
lmx
lxp
こんなもんか? 異様に面倒くさい感じになってしまった。
最小は最大値のコードを
[dlx>c dlx<d z1<m]sm
こんな感じにすればおっけー。
0sx
の部分を修正しないと多分ダメなので注意。
まあそんなわけで。 面白いけれど、少しでも複雑な計算となると実用的じゃない。面倒くさい。 awkのがいいかなぁ…。