BlankTar

about | blog | works | photo

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のがいいかなぁ…。
< linuxのコマンドだけでcsvを取り回してみる lftpのチートシート的なやつ >