dc (desk calculator; 卓上計算機の意) は、任意精度の演算をサポートする、クロスプラットフォームな逆ポーランド記法の計算機ユーティリティである[1]。ロバート・モリスがベル研究所にいる間に開発されたこのソフトウェアは、最も古い Unix ユーティリティのひとつで[2]、これは C 言語の発明よりも前のことである。同世代の他のユーティリティのように一連の強力な機能を持っているが、その構文は簡潔である[3][4]。伝統的に、中間記法の計算機ユーティリティである bc は dc をバックエンドプロセスとして利用していた。
ここでは、言語の一般的な傾向を示すためにいくつかの例を紹介している。完全なコマンドと構文については、それぞれの実装による man ページを参照されたい。
歴史
dc は現存する Unix 言語の中で最も古いものである。ベル研究所に PDP-11 が導入されたときには、アセンブラよりも先に、(B 言語で書かれた) dc がそのマシンで動く最初の言語なった[5]。ケン・トンプソンは、dc がこのマシンで書かれた最初のプログラムだとも発言している[2]。
dc はまた、入力と出力に利用する基数を任意に設定できる。i コマンドは入力時の基数としてスタックをポップした値を設定する。dc コマンドとの衝突を避けるため、利用できる基数は 2 から 16 に限られ、基数に 10 以上を指定する場合は A から F の大文字を数として利用する (小文字は利用できない)。同様に o コマンドは出力時の基数としてスタックからポップした値を設定する。また、現在の精度・入力基数・出力基数を得るためには、K・I・O コマンドを利用する。これらは現在の値をスタックにプッシュする。
dc は、上で述べた基本的な演算やスタック操作に加えて、マクロ、条件分岐、演算結果の一時保存などの機能を備えている。
レジスタ
dc におけるレジスタは、マクロや条件式の基本となる仕組みである。それぞれのレジスタは 1 文字の名前を持っている。レジスタを利用して値を保存したりスタックに取り出したりすることができる。sc コマンドはスタックからポップした要素をレジスタ c に保存する。lc コマンドは c レジスタの値をスタックにプッシュする。以下に示すのはレジスタを利用する例である:
dc では [ と ] で囲まれたものを文字列値として扱う。文字列も数値と同様にスタックにプッシュしたりレジスタに格納したりできる。a コマンドはスタックから要素をポップし、それが数値である場合は下位バイトを ASCII 文字に変換してプッシュし、文字列の場合は最初の 1 文字をプッシュする。文字列を x コマンドでマクロとして実行したり P コマンドで表示することはできるが、文字列を構築したり文字列操作を行う方法はない。
#!/usr/bin/perl -- -export-a-crypto-system-sig Diffie-Hellman-2-lines($g,$e,$m)=@ARGV,$m||die"$0 gen exp mod\n";print`echo "16dio1[d2%Sa2/d0<X+d*La1=z\U$m%0]SX$e"[$g*]\EszlXx+p | dc`
これのコメント付きのバージョンは少しわかりやすく、ループや条件分岐、マクロから脱出するための q コマンドなどの使い方が示されている。GNU の dc では、以下のコード中でレジスタ X に格納されるマクロを使う代わりに | コマンドで冪剰余を演算できる。
#!/usr/bin/perlmy($g,$e,$m)=map{"\U$_"}@ARGV;die"$0 gen exp mod\n"unless$m;print`echo $g $e $m | dc -e '# Hex input and output16dio# Read m, e and g from stdin on one line?SmSeSg# Function z: return g * top of stack[lg*]sz# Function Q: remove the top of the stack and return 1[sb1q]sQ# Function X(e): recursively compute g^e % m# It is the same as Sm^Lm%, but handles arbitrarily large exponents.# Stack at entry: e# Stack at exit: g^e % m# Since e may be very large, this uses the property that g^e % m == # if( e == 0 )# return 1# x = (g^(e/2)) ^ 2# if( e % 2 == 1 )# x *= g# return x %[ d 0=Q # return 1 if e==0 (otherwise, stack: e) d 2% Sa # Store e%2 in a (stack: e) 2/ # compute e/2 lXx # call X(e/2) d* # compute X(e/2)^2 La1=z # multiply by g if e%2==1 lm % # compute (g^e) % m] SXle # Load e from the registerlXx # compute g^e % mp # Print the result'`;