ポタージュを垂れ流す。

マイペースこうしん

dc

dcのアップデート

Macのdcで

1 2 3 3R

とすると

dc: 'R' (0122) unimplemented

と返されてしまった。あれ?と思ったのでバージョンを確認すると

$ dc -V
dc (GNU bc 1.06) 1.3

Copyright 1994, 1997, 1998, 2000 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE,
to the extent permitted by law.

古かった。ブログ書いてる今現在のatcoderではdc1.4.1が使われている。man dcで見るとRが存在していなかったのでRが1.4からの実装だと初めて知る。そこでバージョンを1.4に上げることにした。そのためにはdcが含まれているbcをインストールすることになる。

$ brew install bc

としてしばらく待つとインストールされるのだが

==> Caveats
bc is keg-only, which means it was not symlinked into /usr/local,
because macOS already provides this software and installing another version in
parallel can cause all kinds of trouble.

If you need to have bc first in your PATH run:
  echo 'export PATH="/usr/local/opt/bc/bin:$PATH"' >> ~/.zshrc

==> Summary
🍺  /usr/local/Cellar/bc/1.07.1: 13 files, 340KB

このままだと新たにインストールされたものが普通には起動してくれない

$ which bc
/usr/bin/bc

ので、シンボリックリンクの向き先を変える。

$ brew ls bc
/usr/local/Cellar/bc/1.07.1/bin/bc
/usr/local/Cellar/bc/1.07.1/bin/dc
/usr/local/Cellar/bc/1.07.1/share/info/ (2 files)
/usr/local/Cellar/bc/1.07.1/share/man/ (2 files)

brewシンボリックリンクは/usr/local/binにできるらしいので

$ cd /usr/local/bin
$ ln -s ../Cellar/bc/1.07.1/bin/bc bc
$ ln -s ../Cellar/bc/1.07.1/bin/dc dc

これでシェルを再起動すると

$ dc -V
dc (GNU bc 1.07.1) 1.4.1

Copyright 1994, 1997, 1998, 2000, 2001, 2003-2006, 2008, 2010, 2012-2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE,
to the extent permitted by law.

めでたく最新バージョンのdcが使えるようになる。

使い方とか

数理統計の課題でエクセルとか使うのが面倒だったのでdcでパパっと計算させて結果だけ見たいと思った。

7.97 7.66 7.59 8.44 8.05 8.08 8.35 7.77 7.98 8.15

適当な数10コ用意しました。とりあえず期待値と分散が知りたいので適当にコードを書く。

$ dc
> 7.97 7.66 7.59 8.44 8.05 8.08 8.35 7.77 7.98 8.15
> f
8.15
7.98
7.77
8.35
8.08
8.05
8.44
7.59
7.66
7.97
> zsw0se[d0z-Rle+selw1-dsw0<x]sxlxxzle3kr/p
8.004
> sezsw[le-0z-Rlw1-dsw0<x]sxlxxf
.146
-.024
-.234
.346
.076
.046
.436
-.414
-.344
-.034
> K2*kzsw[d*0z-Rlw1-dsw0<x]sxlxxf
.021316
.000576
.054756
.119716
.005776
.002116
.190096
.171396
.118336
.001156
> [+z1<x]sxlxxK1+k10/p
.0685240

実際に実行したときは>は入らないが見やすさのために入れてみた。dcは入力された数字を上に積むスタックマシンとかいうやつらしい。改行かスペースで数字を分けた後ろ側のものをより上に積む。コマンドは1文字なのでスペースはあってもいいが不要。

簡単に上のプログラムの説明をしてみる。

fはスタックの中身を全部表示。pはスタックの一番上だけを表示(表示した値を捨てることはしない)。

zsw0se[d0z-Rle+selw1-dsw0<x]sxlxxzle3kr/p

zはスタックの高さを取得しその値を一番上に積む。swは変数wに一番上の数を格納する。0を積む。seで変数eに一番上の0を格納(eは後で期待値の値になるつもり)。[ ]内部はwhileループのような構造になっている。 [ dで一番上の数を複製。0を積む。zでスタックの高さを取得しその数積む。-は一番上の数を2番目に上の数から引く(このとき、演算に使われた2つの数はなくなって1つの数が積まれる)。「nR」で上からn番目の数を一番上に持ってくるというコマンドになるので、今は一番上に「マイナス高さ(今回は10)」の数が積まれているから、逆に一番上の数を上から「高さ」分持ち下げる。leはeを読み込んで上に積む。+で一番上の数と2番目に上の数を足す。seでeに一番上の数を格納する。lwでwを読み込み、1を積む。1を一番上にあるwから引いて一番上に積む。dで一番上の数を複製。0を積む。(一番上 0)<(2番目 さっき複製された数)をしてtrueならxを実行する。 ] をsxでxに格納。lxでxを一番上に積む。xは一番上を実行するというコマンドになるので、これで<の部分でfalse判定になるまでこの処理を続ける。 ここまで終わった段階ではちょうど一番最初のスタックの状態になっている。このとき、eには求めたい期待値ではなく、総和が入っているので要素数(=スタックの高さ)で割りたい。のでzで今の要素数を一番上に積み、leでeを呼び出す。ここで間に入る「3k」は一番上に3を積んで、kでその数字の精度を定める(小数点以下3桁まで見るという感じ)。rで一番上と2番目を入れ替える。/で(2番目)/(1番上)をして一番上に積む。ここでpをすると、めでたく期待値が表示される。

次に...とやろうと思ったがもう朝だし説明疲れたのでここまでにする。

ここまで出てこなかったが途中にあるKは現在の精度を一番上に積むというコマンドである。興味があれば「dc - 任意精度の計算機」とかで検索すると使い方は出てくる。

ちなみに、総和を表示させるためだけなら数字をコピペした後

[+z1<x]sxlxxp

期待値を表示するだけなら

[+z1<x]sxlxx3k10/p

とすれば出る。数字を再利用したかったので上で説明したものは少しコードが長くなっている。

...いや、これは綺麗に書きすぎですね、総和は

7.97 7.66 7.59 8.44 8.05 8.08 8.35 7.77 7.98 8.15++++++++++++++++++++++p

で出せる。+は9個必要だが、10個以上になってもstack emptyと怒られるが結果は表示してくれる。期待値にしたければこの後に3k10/pなどとすればよい。

何も考えずコピペして+を押し続けて適当なタイミングでpを押すだけ、ということができるのはdcのいいところの1つだ。アプリとかの電卓だとコピペで全部足すができないだとか、他の言語でやるにもpythonとかだとリストに入れてsumを取る等の手段が考えられるが、リストに入れる時にスペースを,に変えないといけないとか、ちょっと面倒だ。

dcのいいところは非常に短いコードで計算ができるということだ。コードゴルフとかでしばしば見る。使ってると呪文のようでなんとなく楽しい。