bc(1) | General Commands Manual | bc(1) |
bc - 任意精度の計算言語
bc [ -hlwsqv ] [long-options] [ file ... ]
このマニュアルは GNU bc version 1.06 について記述してあります。
bc は、任意の精度の数値を扱う事ができ、プログラミング言語 C の文法に よく似た形の入力を対話的に実行する言語です。 コマンドラインのオプションの指定により、標準数学ライブラリを使用することも できます。これを指定した場合は、どのファイルを処理するよりも前に 数学ライブラリが定義されます。 bc は動作を開始するとまず最初にコマンドラインで指定したファイルを 順に処理します。すべてのファイルを処理した後は、bc は 標準入力からの読み込みを行います。すべてのコードは、それが読み込ま れた時点で実行されていきます。(もし、ファイル中にプロセッサを止める コマンドが含まれていた場合は、標準入力からの読み込みは行われません。)
本バージョンの bc は、伝統的な bc の実装および POSIX のドラフト規格よりも拡張されています。コマンドラインオプションにより、 これらの拡張に対して警告を表示したり拒絶したりすることが可能です。 本ドキュメントでは、このプロセッサが受理する言語について説明します。 拡張機能についてはその旨明記します。
bc における最も基本的な要素は `数' です。数は、整数部と小数部があり、 任意の精度をとることができます。すべての数は、内部では 10 進数で表現されており、 計算も 10 進数で行われます。(本バージョンでは、除算と乗算で結果に切捨てが 起こります。) 数には length と scale という 2 つの属性があります。 length は 10 進での有効桁数で、scale は小数点以下の 10 進での有効桁数です。 例えば、
.000001 は、lengthが 6 で、scale も 6 です。
1935.000 は、lengthが 7 で、scale が 3 です。
数は、単純変数と配列の 2 種類の変数に保存されます。単純変数と配列変数には共に 名前が付けられます。この名前は、最初の 1 文字目がアルファベットで、後は、 アルファベット、数字およびアンダスコアを任意の文字数組み合わせて 使うことができます。すべてのアルファベットは小文字でなければなりません。 (アルファベットと数字を使った名前の機能は拡張機能です。 POSIX bc では、変数に英小文字 1 文字しか許されません。) 配列変数の名前には必ずブラケット ([]) がつくので、変数の型は文脈において はっきりしています。
特殊な変数として scale, ibase, obase, last の 4 つの変数があります。 scale で計算時の小数点以下の有効桁数を指定します。 scale のデフォルトは 0 です。 ibase と obase で入力および出力の変換基数を指定します。 デフォルトでは、入力、出力の基数は共に 10 です。 last は、最後に bc が出力した数を保持しています (これは拡張機能です)。これらについては、後で適切なところで詳しく説明します。 これらの変数には、式で使われる代入と同様の代入を行うことが可能です。
bc は、/* から */ の間をコメントとして扱います。 コメントはどこから始まっていてもよく、1 文字の空白として扱われます。 (これにより、コメントはその前後の入力アイテムを切り離します。たとえば、 変数名の途中にコメントを置くことはできません。) コメントの中にはいくつ改行があってもかまいません。
bc をスクリプトとしても使えるようにするため、1 行コメントが 拡張機能として追加されました。1 行コメントは # で始まり、 次の改行まで有効です。その改行文字自体はコメントの一部とはみなされず、 普通に処理されます。
`数' は、式および文によって操作されます。 この言語は対話的になるように設計されているため、 文および式は可能な限り即座に実行されます。 "main" プログラムといったものはなく、そのかわり、コードは それに出くわした時点で実行されます。 (後で述べる`関数'は、それに出くわした時点で定義されます。)
式の最も単純なものは、ただの定数です。bc は、入力された 定数を、変数 ibase で指定される現在の基数を元に、内部的には 10 進表現の 数に変換します。(関数の場合には例外があります。) ibase には、2 から 16 までが使用できます。 この範囲を越える値を ibase に代入しようとすると、 2 あるいは 16 を指定したことになります。 数の入力には、0-9 および A-F の文字が利用できます。(注意: これは大文字でなければなりません。小文字は変数名です。) 1 桁の数は ibase の値に関係なくその値を持ちます (すなわち A=10)。 複数桁の数の場合、bc は ibase 以上の値をもつすべての入力桁を ibase-1に変更します。これにより、数 FFF は常に、 その入力基数を使って 3 桁で表現可能な最大の値を表します。
すべての演算式が、他の多くの高級言語に似たものとなっています。 数の型は 1 種類しかないため、型変換の規則はありません。 そのかわり、式の有効桁数に関する規則があります。 すべての式に有効桁数があり、これはその被演算数の有効桁数と 施される演算、それに多くの場合、 変数 scale から決定されます。scale には、0 から C の整数で表現できる最大の値までが指定可能です。
以下、bc で使用可能な演算子を説明します。なお、完全形の式を "expr"、 単純変数または配列変数を "var" と表記します。 単純変数は単に
関係演算は特殊な演算で、結果は常に 0 か 1 になります。関係が偽の時 0、 真の時 1 になります。関係演算は、演算式のどこでも使う事ができます。 (POSIX bcでは、関係演算は、if, while, for 文の中だけで、しかも 1 つの関係式しか使用できません。) 関係演算子は以下の通り。
論理演算も使えます。(POSIX bc には論理演算はありません。) 論理演算も関係演算と同様、結果は 0 か 1 (各々偽および真) になります。 論理演算子は以下の通り。
各演算子の優先順位と結合規則は次の通りです。 (最初のものほど低く、後にいくほど高い優先順位で先に実行されます。)
|| (左から結合) && (左から結合) ! (結合せず) 関係演算 (左から結合) 代入演算 (右から結合) + - (左から結合) * / % (左から結合) ^ (右から結合) - (単項マイナス) (結合せず) ++ -- (結合せず)
この優先順位は、POSIX bc のプログラムがそのまま正しく動くように 配慮して決められています。このため、関係演算と論理演算を 代入文と共に用いた場合、通常とは異なる振る舞いをします。 次の例を考えてみましょう:
C プログラマのほとんどは、 ``3 < 5'' の関係演算が実行された結果 (つまり 1) が変数 ``a'' に代入される、 と考えるでしょう。 ところが bc では、まず 3 が変数 ``a'' に代入され、 それから 3 と 5 の比較が行われるのです。 この間違いを避けるために、 関係演算や論理演算を代入演算と共に用いる場合は、 括弧を使うのが最良です。
bc には特別な式がさらにいくつか備わっています。 それはユーザ定義関数と標準関数に関するもので、 すべて "name(parameters)" という形をしています。 ユーザ定義関数については関数の章を参照して下さい。 標準関数は以下の通りです:
文は (ほとんどの算術言語がそうであるように)、処理を順番に実行していく単位です。 bc では文は「できるだけ早い段階で」実行されます。 改行が入力された時点で、実行可能な文が存在していれば、即座に実行します。 このため bc では改行が重要な役割を持っています。 実際、セミコロンと改行が文の区切りとして使用されます。 不適当な場所で改行を入力すると、文法エラーになります。 改行は文の区切りですが、バックスラッシュを用いて改行を隠すことができます。 bc にとって、"\<nl>" (<nl>は改行) は改行ではなく空白に見えます。 文のリストは、セミコロンと改行で区切られた文の並びです。 以下、bc の文の種類とその動作について説明します。 (なお、以下の説明で ([]) で括った部分は省略可能な項です。)
expression1; while (expression2) {
statement;
expression3; }
これらは今までの文とは動作が異なります。 疑似文は実行文ではなく、「コンパイル」時点で処理されます。
関数は、後で実行されるべき計算手順を定義する機能です。 bc の関数は常に値を計算し、それを呼びだし側に返します。 関数定義は、それが入力から読み込まれた時点で定義が行われるという点で 「ダイナミック(動的)」です。 一度定義された関数は、同じ名前で別の関数が定義されるまで使用可能で、 新しい関数が定義された場合は、前の関数が置き換えられます。 関数の定義は、以下のように行います:
define name ( parameters ) { newline auto_list statement_list }
関数呼び出しは、 "name(parameters)" という形式の演算式です。
パラメータ parameters は数あるいは配列 (拡張機能) です。 関数定義では、0 あるいは 1 個以上のパラメータ名を コンマで区切って並べることで定義します。 数は値渡し(call by value)でのみ渡され、配列は変数渡し(call by variable)で のみ渡されます。 配列はパラメータ定義中で "name[]" のように表記して指定します。 関数呼び出しでは、数のパラメータに対して完全な演算式の実パラメータを 記述します。 配列を渡す表記は配列パラメータ定義と同様です。 名前付き配列は変数(variable)によって関数に渡されます。 関数定義はダイナミックゆえ、 パラメータの数と型は関数呼び出しの際にチェックされます。 パラメータの数あるいは型に何らかの不整合があると、 ランタイムエラーが発生します。 未定義関数を呼び出した場合もランタイムエラーとなります。
auto_list は省略可能で、ローカル変数として使用する変数のリスト です。auto_list が存在するなら、その文法は "auto name, ... ;" となります。(セミコロンは省略可能です。) 各 name がローカル変数の名前となります。 配列はパラメータと同様の表記で指定できます。 これらの変数は、関数の最初でその値がスタックにプッシュされたのち 値 0 に初期化され、関数の実行中に使用されます。 これらの変数は関数出口にてポップされ、 (関数呼び出し時の)元の値が復元されます。 パラメータは実際にはローカル変数であり、 関数呼び出しで与えられた値に初期化されます。 bc のローカル変数は伝統的な意味でのローカル変数と異なり、 関数 A が関数 B を呼び出しているような場合、関数 B の中に 関数 A のローカル変数と同じ名前のローカル変数がない限り、 関数 A のローカル変数名をそのまま使って、 関数 B から関数 A のローカル変数をアクセスできます。 ローカル変数とパラメータはスタックにプッシュされるため、 bc は再帰的な関数呼び出しをサポートしています。
関数本体は bc の文のリストです。 繰り返し述べますと、文はセミコロンか改行で区切られています。 return 文により関数は終了し、値を返します。 return 文には 2 つの形式があり、 ひとつめの形式 "return" は、呼び出し元に値 0 を返します。 もうひとつの形式 "return ( expression )" は、 expression の値を計算し、それを呼び出し元に返します。 各関数の最後には "return (0)" があるものと解釈されます。 これにより、明示的に return 文を置かなくても、 関数は終了して値 0 を返します。
関数の中では、変数 ibase の動作が変わります。関数の中で使われて いる定数は、関数の呼びだし時点の ibase を元に変換が行われます。 このため、関数内部で ibase を変更しても無視されます。ただし、標 準関数 read を呼び出した場合は例外で、これは常に現在の ibase の値をもとに変換が行われます。
拡張機能ですが、定義の書式が若干緩やかになりました。 標準では、開くブレースが define キーワードと同じ行にあることと、 他の部分が引き続く行にあることが必須です。 本バージョンの bc では、関数の開くブレースの前後の改行数は任意です。 例えば、次の定義は合法です。
define d (n) { return (2*n); } define d (n)
{ return (2*n); }
bc に -l オプションを付けて起動した場合は、数学ライブラリが 読み込まれ、デフォルトの scale が 20 に設定されます。 数学関数は、それを呼び出した時点の scale の値に従って計算を行います。 数学ライブラリによって使用可能になる関数は、次の通りです:
次の例は、/bin/sh でシェル変数 pi に ``パイ'' の値を代入します。
次の例は、数学ライブラリで使われている ``e (x)'' の定義です。 この関数は POSIX bc で記述されています。
scale = 20 /* Uses the fact that e^x = (e^(x/2))^2
When x is small enough, we use the series:
e^x = 1 + x + x^2/2! + x^3/3! + ... */ define e(x) {
auto a, d, e, f, i, m, v, z
/* Check the sign of x. */
if (x<0) {
m = 1
x = -x
}
/* Precondition x. */
z = scale;
scale = 4 + z + .44*x;
while (x > 1) {
f += 1;
x /= 2;
}
/* Initialize the variables. */
v = 1+x
a = x
d = 1
for (i=2; 1; i++) {
e = (a *= x) / (d *= i)
if (e == 0) {
if (f>0) while (f--) v = v*v;
scale = z
if (m) return (1/v);
return (v/1);
}
v += e
} }
次の例は、bc の拡張機能を使って、``checkbook balances'' (小切手帳残高) を計算する簡単なプログラムです。 このプログラムをファイルにしておくと、 毎回タイプしなおさずに何度も使うことができます。
scale=2 print "\nCheck book program!\n" print " Remember, deposits are negative transactions.\n" print " Exit by a 0 transaction.\n\n" print "Initial balance? "; bal = read() bal /= 1 print "\n" while (1) {
"current balance = "; bal
"transaction? "; trans = read()
if (trans == 0) break;
bal -= trans
bal /= 1 } quit
次の例は、再帰呼び出しにより階乗を計算する関数です。
define f (x) {
if (x <= 1) return (1);
return (f(x-1) * x); }
GNU bc は (configure のオプションによって) GNU readline 入力エディタライブラリまたは BSD libedit ライブラリ を使うようにコンパイルできます。 これは、bc に入力する前に、行の編集を可能にします。 以前に入力した行のヒストリも利用可能になります。このオプションで コンパイルされた bc では、さらに 1 つの特殊な変数 history が追加され、ヒストリに保存される行の数を指定します。 readline では、 その値が -1 (デフォルト値)なら、ヒストリ行は制限なく保存されます。 正の数を指定すると、ヒストリ行がその数に制限されます。 0 ならヒストリ機能が無効になります。 デフォルト値は 100 です。 詳しくは、ユーザマニュアルの GNU readline と history ライブラリと BSD libedit をご覧下さい。 readline と libedit の両方を同時に有効化できません。
このバージョンの bc は POSIX P1003.2/D11 ドラフトから実装されており、 そのドラフトや以前の実装に比べていくつかの相違点や拡張点があります。 伝統的に行われていたような dc(1) を用いた実装ではありません。 このバージョンは単一プロセスであり、 プログラムをバイトコードに変換したものを解析して実行します。 「ドキュメントに記載されていない」オプション (-c) があり、 プログラムを実行する代わりに、それをバイトコードに変換した結果を 標準出力に出力します。 これは主として、パーザのデバッグと数学ライブラリの準備に用いられました。
主な相違点は拡張機能によるものです。 機能を高めたり追加したりするために機能が拡張されたり、 新機能が追加されたりしています。 相違点と拡張点のリストを以下に示します。
a = 1 b = 2
{ a = 1
b = 2 }
には 1 つの実行ブロックがあります。 ランタイムエラーが発生すると、現在の実行ブロックの実行が終了します。 ランタイムの警告が発生しても、現在の実行ブロックは終了しません。
以下の項目が現在の bc プロセッサの限界値となっています。 このうちいくつかは、インストール時に変更できます。 実際の値を得るには limits 文を使って下さい。
bc は以下の環境変数を解釈します。
コマンドラインで指定したファイルがオープンできない場合、 bc はファイルが利用できない旨を表示して終了します。 また、コンパイル時あるいはランタイムの診断メッセージもありますが、 それらは自身で理解できるようになっているはずです。
エラーリカバリがまだうまくいっていません。
バグ報告は、 bug-bc@gnu.org に電子メールでお願いします。 単語 ``bc'' を ``Subject:'' フィールドのどこかに入れておいてください。
Philip A. Nelson philnelson@acm.org
実装をテストする際に 広範囲に手助けしてくれた Steve Sommars (Steve.Sommars@att.com) に感謝します。 たくさんの素晴らしい意見をもらいました。 彼のおかげでとてもよいものになりました。
. |