Dc (UNIX)のソースを表示
←
Dc (UNIX)
ナビゲーションに移動
検索に移動
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループに属する利用者のみが実行できます:
登録利用者
。
このページのソースの閲覧やコピーができます。
{{lowercase|title=dc (UNIX)}} {{Infobox Software | 名称 = dc | 開発者 = ロバート・H・モリス<br />([[ベル研究所]]) | プログラミング言語 = [[B言語]] | 対応OS = [[UNIX]], [[Unix系]], [[Plan 9 from Bell Labs|Plan 9]] | 対応プラットフォーム = [[クロスプラットフォーム]] | 種別 = [[コマンド (コンピュータ)|コマンド]] }} '''dc''' (''desk calculator''; 卓上計算機の意) は、[[任意精度演算|任意精度の演算]]をサポートする、[[クロスプラットフォーム]]な[[逆ポーランド記法]]の計算機ユーティリティである<ref>{{man|1|dc|die.net|an arbitrary precision calculator}}</ref>。[[:en:Robert_Morris_(cryptographer)|ロバート・モリス]]が[[ベル研究所]]にいる間に開発されたこのソフトウェアは、最も古い [[UNIX|Unix]] ユーティリティのひとつで<ref name="KenThompson">{{cite av media|url=https://www.youtube.com/watch?v=EY6q5dv_B-o|people=Brian Kernighan and Ken Thompson|title=A nerdy delight for any Vintage Computer Fest 2019 attendee: Kernighan interviewing Thompson about Unix|accessdate=September 3, 2019|publisher=YouTube|time=29m45s}}</ref>、これは [[C言語|C 言語]]の発明よりも前のことである。同世代の他のユーティリティのように一連の強力な機能を持っているが、その構文は簡潔である<ref>{{cite web|url=https://web.archive.org/web/20170601064221/http://plan9.bell-labs.com/7thEdMan/vol2/dc|title=The sources for the manual page for 7th Edition Unix dc (Internet Archive)|accessdate=2021-03-16}}</ref><ref>{{cite web|author=Ritchie, Dennis M.|date=Sep 1979|url=http://cm.bell-labs.com/cm/cs/who/dmr/hist.html|title=The Evolution of the Unix Timesharing System|url-status=dead|archive-url=http://webarchive.loc.gov/all/20100506231949/http://cm.bell-labs.com/cm/cs/who/dmr/hist.html|archive-date=2010-05-06|accessdate=2021-03-16}}</ref>。伝統的に、[[中置記法|中間記法]]の計算機ユーティリティである [[Bc (UNIX)|bc]] は dc をバックエンドプロセスとして利用していた。 ここでは、言語の一般的な傾向を示すためにいくつかの例を紹介している。完全なコマンドと構文については、それぞれの実装による [[Manページ|man ページ]]を参照されたい。 == 歴史 == dc は現存する [[UNIX|Unix]] 言語の中で最も古いものである。[[ベル研究所]]に [[PDP-11]] が導入されたときには、アセンブラよりも先に、([[B言語|B 言語]]で書かれた) dc がそのマシンで動く最初の言語なった<ref name="reader3">{{cite techreport|first1=M. D.|last1=McIlroy|authorlink1=Doug McIlroy|year=1987|url=http://www.cs.dartmouth.edu/~doug/reader.pdf|title=A Research Unix reader: annotated excerpts from the Programmer's Manual, 1971–1986|series=CSTR|number=139|institution=Bell Labs}}</ref>。[[ケン・トンプソン]]は、dc がこのマシンで書かれた最初のプログラムだとも発言している<ref name="KenThompson" />。 == 基本的な演算 == dc で <math>4 \times 5</math> を計算するには次のように実行する ([[スペース|空白文字]]のほとんどは省略可能):<syntaxhighlight lang="console"> $ cat << EOF > cal.txt 4 5 * p EOF $ dc cal.txt 20 $ </syntaxhighlight>次のコマンドを使用して結果を得ることもできる:<syntaxhighlight lang="console"> $ echo "4 5 * p" | dc </syntaxhighlight>あるいは:<syntaxhighlight lang="console"> $ dc - 4 5*pq 20 $ dc 4 5 * p 20 q $ dc -e '4 5 * p' </syntaxhighlight>これらの動作は「4 と 5 を[[スタック]]にプッシュし、乗算演算子 (<code>*</code>) によってスタックから要素を 2 つポップし、それらをかけ合わせた値をスタックにプッシュする」ものである。<code>p</code> コマンドはスタックの先頭の要素を表示する。<code>q</code> コマンドは dc プログラムを終了する。演算子同士や演算子と数字の間のスペースは省略できるが、数字同士の間のスペースは省略できないことに注意する。また、数値は <code>3.14</code> や <code>.318</code> などの小数であってもよいが、指数表記 (1.23e4 のような表記) には対応していない。 [[有効数字|演算の精度]] (演算で有効とする小数点以下の桁数) を変更するには <code>k</code> コマンドを利用する。起動時のデフォルト値は 0 であるので、次の dc コマンドの結果は '''<code>0</code>''' となる: 2 3 / p <code>k</code> コマンドを利用して適当な精度に設定することで、任意の小数点以下の桁を演算できる。次の dc コマンドの結果は '''<code>.66666</code>''' となる: 5 k 2 3 / p 次の dc コマンドは <math>\sqrt{\frac{12+(-3)^4}{11}}-22</math> を計算する (<code>v</code> コマンドはスタックの先頭の平方根を演算し、<code>_</code> は負値の負符号である): 12 _3 4 ^ + 11 / v 22 - p その他にも、スタックの先頭 2 つの要素の順を入れ替える <code>r</code> コマンドやスタックの先頭の要素を複製する <code>d</code> コマンドなどがある。 == 入出力 == <code>?</code> コマンドを利用して[[標準ストリーム|標準入力]]を 1 行読み込むことができる。このコマンドは入力された行を dc コマンドとして評価する。そのため、入力は dc コマンドとして正しい構文である必要があるほか、<code>!</code> コマンドを利用して任意のユーザコマンドを実行できるため、セキュリティ上の問題が発生する恐れすらある。 前述したとおり、<code>p</code> コマンドはスタックの先頭の要素を表示して改行する。<code>n</code> コマンドはスタックからポップした値を改行を伴わずに表示する。<code>f</code> コマンドはスタックの内容を先頭から順にすべて表示する。 dc はまた、入力と出力に利用する[[位取り記数法|基数]]を任意に設定できる。<code>i</code> コマンドは入力時の基数としてスタックをポップした値を設定する。dc コマンドとの衝突を避けるため、利用できる基数は 2 から 16 に限られ、基数に 10 以上を指定する場合は A から F の大文字を数として利用する (小文字は利用できない)。同様に <code>o</code> コマンドは出力時の基数としてスタックからポップした値を設定する。また、現在の精度・入力基数・出力基数を得るためには、<code>K</code>・<code>I</code>・<code>O</code> コマンドを利用する。これらは現在の値をスタックにプッシュする。 以下に示すのは 16 進数の入力を 2 進数に変換する例である:<syntaxhighlight lang="console"> $ echo 16i2o DEADBEEF p | dc 11011110101011011011111011101111 </syntaxhighlight> == 言語の特徴的な機能 == dc は、上で述べた基本的な演算やスタック操作に加えて、[[マクロ (コンピュータ用語)|マクロ]]、[[分岐命令|条件分岐]]、演算結果の一時保存などの機能を備えている。 === レジスタ === dc におけるレジスタは、マクロや条件式の基本となる仕組みである。それぞれのレジスタは 1 文字の名前を持っている。レジスタを利用して値を保存したりスタックに取り出したりすることができる。<code>s''c''</code> コマンドはスタックからポップした要素をレジスタ ''c'' に保存する。<code>l''c''</code> コマンドは ''c'' レジスタの値をスタックにプッシュする。以下に示すのはレジスタを利用する例である: 3 sc 4 lc * p レジスタをスタックとして利用することもできる。メインのスタックとレジスタのスタックとの間でプッシュ・ポップするには <code>S</code>・<code>L</code> コマンドを利用する。 === 文字列 === dc では <code>[</code> と <code>]</code> で囲まれたものを文字列値として扱う。文字列も数値と同様にスタックにプッシュしたりレジスタに格納したりできる。<code>a</code> コマンドはスタックから要素をポップし、それが数値である場合は下位バイトを [[ASCII|ASCII 文字]]に変換してプッシュし、文字列の場合は最初の 1 文字をプッシュする。文字列を <code>x</code> コマンドでマクロとして実行したり <code>P</code> コマンドで表示することはできるが、文字列を構築したり文字列操作を行う方法はない。 また、dc では <code>#</code> から行末までを[[コメント (コンピュータ)|コメント]]として扱い、実行には影響を及ぼさない。 === マクロ === dc ではレジスタやスタックに数値だけでなく、文字列を格納できるようにすることでマクロ機能を実現している。文字列は表示するだけでなく、それ自身を dc コマンドとして実行することもできる。以下に示すのは 1 を加算して 2 を掛けるマクロをレジスタ m に格納する例である: [1 + 2 *] sm そして次のようにマクロを呼び出す (<code>x</code> コマンドはスタックからポップした値をマクロとして実行する): 3 lm x p === 条件分岐 === マクロの仕組みを利用して条件分岐を行うことができる。<code>=''r''</code> コマンドはスタックから要素を 2 つポップし、それらの値が等しいときにレジスタ ''r'' に格納されているマクロを実行する。以下に示すのはスタックの先頭に 5 があるときに '''<code>Equal</code>''' と表示する例である: <nowiki>[[Equal]p]sm 5 =m</nowiki> その他にも、<code>></code> (もともとの先頭が大きい場合にマクロ実行)、<code>!></code> (大きくない場合)、<code><</code> (小さい場合)、<code>!<</code> (小さくない場合)、<code>!=</code> (等しくない場合) がある。 === ループ === dc でループそのものを行う方法はないが、マクロを[[再帰#再帰呼出し|再帰的]]に実行することで再現できる。スタックの先頭の要素の階乗を求めるには次のようにする: # F(x): return x! # if x-1 > 1 # return x * F(x-1) # otherwise # return x [d1-d1<F*]dsFxp <code>1Q</code> コマンドはマクロから脱出する。これに対して <code>q</code> コマンドはそのマクロとそのマクロを呼び出したマクロから脱出する。マクロの呼び出し深度が 2 未満の場合は dc プログラムをも終了する。<code>z</code> コマンドは現在の (<code>z</code> コマンドの実行前の) スタックの深さをプッシュする。 == 例 == === スタック全体の合計 === これはレジスタ a に格納されたマクロを再帰的に呼び出してスタック全体の合計を計算する。マクロの動作は、まずスタックの先頭 2 を足し合わせ (<code>+</code>)、スタックの深さが 1 より大きい場合は再帰する (<code>1z<a</code>) ものである。マクロ文直前の <code>0d</code> は一見必要ないように思えるがが、加算演算子がスタックの先頭 2 の要素を確実に取り出せることを保証するために必要である。 1 2 4 8 16 100 # 加算したい数をスタックに積む 0d[+z1<a]dsaxp # すべて加算して表示 実行結果は <code>'''131'''</code> となる。 === ファイル中の dc コマンドの合計 === ファイルの各行に記述された dc コマンドを実行し、その合計値を計算する。単なる数値は dc コマンドとして有効であるので、行ごとに記述された数値の合計を得ることもできる。 これも上の例と同様にレジスタ a に格納されたマクロを再帰的に呼び出して合計を計算している。<syntaxhighlight lang="console"> $ cat file | dc -e '0d[?+z1<a]dsaxp' </syntaxhighlight><code>?</code> コマンドは標準入力から dc コマンドを読み込み、結果をスタックにプッシュする。ファイルの終端に達した場合はコマンドは読み込まれず、スタックには何もプッシュされない。<syntaxhighlight lang="console"> $ { echo "5"; echo "7"; } | dc -e '0d[?+z1<a]dsaxp' </syntaxhighlight>この結果は <code>'''12'''</code> となる。 入力の dc コマンドは複雑でも良い。<syntaxhighlight lang="console"> $ { echo "3 5 *"; echo "4 3 *"; echo "5dd++"; } | dc -e '0d[?+z1<a]dsaxp' </syntaxhighlight>この結果は <math>(3\times5)+(4\times3)+(5+5+5) = 42</math> より <code>'''42'''</code> となる。 この手法を利用することの長所は、dc の演算は[[任意精度演算|無限精度]]であるので、 [[AWK]] などの簡明な手法を用いる場合に起こりうる[[算術オーバーフロー|オーバーフロー]]や精度落ちが発生しないことである。 対して短所もあり、空行 (技術的には要素をプッシュしない行) に遭遇するとループが停止すること、負値の符号 <math>-</math> を dc の負符号 <code>_</code> に置換する必要があることが挙げられる。dc の <code>?</code> には空行とファイル終端を見分けるきれいな方法が提供されていない。 === 単位変換 === 以下に示すのは、ワンライナーで書かれた較的簡単な単位変換プログラムの例である:<syntaxhighlight lang="console"> $ dc -e '[[Enter a number (metres), or 0 to exit]psj]sh[q]sz[lhx?d0=z10k39.370079*.5+0k12~1/rn[ feet ]Pn[ inches]P10Pdx]dx' </syntaxhighlight>この例はメートルで表された距離をフィートとインチに変換する。ユーザに入力を促し、適切な形式で変換結果を出力し、別の数値を変換するためにループしている。 === 最大公約数 === 以下に示すのは[[ユークリッドの互除法|互除法]]を利用して最大公約数を求める例である:<syntaxhighlight lang="console"> $ dc -e '??[dSarLa%d0<a]dsax+p' # 最短実装 $ dc -e '[a=]P?[b=]P?[dSarLa%d0<a]dsax+[GCD:]Pp' # 読みやすくしたもの </syntaxhighlight> === 階乗 === 入力された値 <math>n</math> の階乗 <math>n! = \prod_{k=1}^{n}{k}</math> を計算する:<syntaxhighlight lang="console"> $ dc -e '?[q]sQ[d1=Qd1-lFx*]dsFxp' </syntaxhighlight> === Quine === dc にも [[クワイン (プログラミング)|quine]] が存在する。<syntaxhighlight lang="console"> $ dc -e '[91Pn[dx]93Pn]dx' $ dc -e '[91PP93P[dx]P]dx' </syntaxhighlight> === すべての素数を求める === <syntaxhighlight lang="console"> $ echo '2p3p[dl!d2+s!%0=@l!l^!<#]s#[s/0ds^]s@[p]s&[ddvs^3s!l#x0<&2+l.x]ds.x' | dc </syntaxhighlight>このプログラムは Michel Charpentier によって書かれた、すべての素数を表示するプログラムである。このプログラムは次のように短縮でき、恐らくこれが最短であると思われる。<syntaxhighlight lang="console"> $ echo '2p3p[dl!d2+s!%0=@l!l^!<#]s#[0*ds^]s@[p]s&[ddvs^3s!l#x0<&2+l.x]ds.x' | dc </syntaxhighlight> === 素因数分解 === これもまた Michel Charpentier によって書かれた<ref>{{cite web|url=https://tldp.org/LDP/abs/html/mathc.html|title=Advanced Bash-Scripting Guide, Chapter 16, Example 16-52 (Factorization)|access-date=2020-09-20}}</ref>:<syntaxhighlight lang="console"> $ dc -e '[n=]P?[p]s2[lip/dli%0=1dvsr]s12sid2%0=13sidvsr[dli%0=1lrli2+dsi!>.]ds.xd1<2' </syntaxhighlight>短くすると:<syntaxhighlight lang="console"> $ dc -e "[n=]P?[lfp/dlf%0=Fdvsr]sF[dsf]sJdvsr2sf[dlf%0=Flfdd2%+1+sflr<Jd1<M]dsMx" </syntaxhighlight>より高速なものでは (200 bit の数 <math>2^{200} - 1</math> で試すなら <code>2 200^1-</code> を入力する):<syntaxhighlight lang="console"> $ dc -e "[n=]P?[lfp/dlf% 0=Fdvsr]sFdvsr2sfd2%0=F3sfd3%0=F5sf[dlf%0=Flfd4+sflr>M]sN[dlf%0=Flfd2+sflr>N]dsMx[p]sMd1<M" </syntaxhighlight>これを更に高速化するには、定数へのアクセスをレジスタへのアクセスに置き換える:<syntaxhighlight lang="console"> $ dc -e "[n=]P?[lfp/dlf%l0=Fdvsr]sF2s2dvsr2sf4s4d2%0=F3sfd3%0=F5sf[dlf%l0=Flfdl4+sflr>M]sN[dlf%l0=Flfdl2+sflr>N]dsMx[p]sMd1<M" </syntaxhighlight> === Diffie–Hellman 鍵交換 === より複雑な例では、Perl スクリプトに組み込まれている [[ディフィー・ヘルマン鍵共有|Diffie–Hellman 鍵交換]]がある。これは ITAR 議論のときに[[サイファーパンク]]たちの間で人気であった。このスクリプトは [[Unix系|Unix ライク]]な OS にはどこにでもある Perl と dc だけで実行できる<ref>{{cite web|url=http://www.cypherspace.org/adam/rsa/perl-dh.html|title=Diffie–Hellman in 2 lines of Perl|access-date=5 Jan 2009|author=Adam Back}}</ref>:<syntaxhighlight lang="perl"> #!/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` </syntaxhighlight>これのコメント付きのバージョンは少しわかりやすく、ループや条件分岐、マクロから脱出するための <code>q</code> コマンドなどの使い方が示されている。GNU の dc では、以下のコード中でレジスタ X に格納されるマクロを使う代わりに <code>|</code> コマンドで冪剰余を演算できる。<syntaxhighlight lang="perl"> #!/usr/bin/perl my ($g, $e, $m) = map { "\U$_" } @ARGV; die "$0 gen exp mod\n" unless $m; print `echo $g $e $m | dc -e ' # Hex input and output 16dio # 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 ] SX le # Load e from the register lXx # compute g^e % m p # Print the result '`; </syntaxhighlight> == 関連項目 == * [[bc (UNIX)]] * [[ヒューレット・パッカードの電卓]] * [[スタックマシン]] == 脚注・出典 == <references /> == 外部リンク == * [[Debian|Debian GNU/Linux]] レポジトリの [https://packages.debian.org/search?keywords=dc&searchon=names&exact=1&suite=all§ion=all dc] パッケージ * [http://man.cat-v.org/plan_9/1/dc dc(1)] – [[Plan 9 from Bell Labs|Plan 9]] Programmer's Manual, Volume 1 *[https://linuxjm.osdn.jp/html/GNU_bc/man1/dc.1.html Man page of DC] – JM Project *[https://gnuwin32.sourceforge.net/packages/bc.htm Bc for Windows] (dc を含む) * dc の web 実装: http://dc.pr0.uk/ {{Unixコマンド}} [[Category:UNIXのソフトウェア]] [[Category:クロスプラットフォームのソフトウェア]]
このページで使用されているテンプレート:
テンプレート:Cite av media
(
ソースを閲覧
)
テンプレート:Cite techreport
(
ソースを閲覧
)
テンプレート:Cite web
(
ソースを閲覧
)
テンプレート:Infobox Software
(
ソースを閲覧
)
テンプレート:Lowercase
(
ソースを閲覧
)
テンプレート:Man
(
ソースを閲覧
)
テンプレート:Unixコマンド
(
ソースを閲覧
)
Dc (UNIX)
に戻る。
ナビゲーション メニュー
個人用ツール
ログイン
名前空間
ページ
議論
日本語
表示
閲覧
ソースを閲覧
履歴表示
その他
検索
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
特別ページ
ツール
リンク元
関連ページの更新状況
ページ情報