任意精度の計算言語
は、lengthが 6 で、scale も 6 です。 1935.000 は、lengthが 7 で、scale が 3 です。
|| (左から結合) && (左から結合) ! (結合せず) 関係演算 (左から結合) 代入演算 (右から結合) + - (左から結合) * / % (左から結合) ^ (右から結合) - (単項マイナス) (結合せず) ++ -- (結合せず)この優先順位は、POSIX bc のプログラムがそのまま正しく動くように 配慮して決められています。このため、関係演算と論理演算を 代入文と共に用いた場合、通常とは異なる振る舞いをします。 次の例を考えてみましょう: a = 3 < 5 C プログラマのほとんどは、 ``3 < 5'' の関係演算が実行された結果 (つまり 1) が変数 ``a'' に代入される、 と考えるでしょう。 ところが bc では、まず 3 が変数 ``a'' に代入され、 それから 3 と 5 の比較が行われるのです。 この間違いを避けるために、 関係演算や論理演算を代入演算と共に用いる場合は、 括弧を使うのが最良です。 bc には特別な式がさらにいくつか備わっています。 それはユーザ定義関数と標準関数に関するもので、 すべて name(parameters) という形をしています。 ユーザ定義関数については関数の章を参照して下さい。 標準関数は以下の通りです: length ( expression ) expression の有効桁数を返します。 read ( ) (拡張機能) 関数の出現位置に関係なく、標準入力から数を読み取ります。 データとプログラムの両方を標準入力から与えるような場合には、 問題を生じうることに注意して下さい。 最良の方法は、 ユーザからデータの入力の必要があるなら、プログラムはあらかじめ作っておき、 標準入力からプログラムを入力しないようにすることです。 read 関数の値は標準入力から読み込んだ数です。 その際、変換基数として変数 ibase の現在の値が用いられます。 scale ( expression ) expression の小数点以下の有効桁数を返します。 sqrt ( expression ) expression の平方根を返します。 expression に負の値を指定した場合は、ランタイムエラーになります。
expression1; while (expression2) { statement; expression3; }break それを含む最も内側の while もしくは for 文による繰り返しを強制的に中断します。 continue それを含む最も内側の for 文における次の繰り返しに進みます。 (continue 文は拡張機能です) halt 実行されると bc プロセッサを終了させます(拡張機能)。 例えば if (0 == 1) halt の場合は bc は終了しません。 halt 文が実行されないからです。 return 関数から戻ります。関数の結果は 0 になります。(関数の章を参照) return ( expression ) 関数から戻ります。関数の結果は expression になります。(関数の章を参照) 拡張機能ですが、括弧は必須ではありません。
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 では、関数の開くブレースの前後の改行数は任意です。 例えば、次の定義は合法です。
\f(CW define d (n) { return (2*n); } define d (n) { return (2*n); }
\f(CW scale = 20次の例は、bc の拡張機能を使って、``checkbook balances'' (小切手帳残高) を計算する簡単なプログラムです。 このプログラムをファイルにしておくと、 毎回タイプしなおさずに何度も使うことができます。
/* 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 } }
\f(CW 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
\f(CW define f (x) { if (x <= 1) return (1); return (f(x-1) * x); }
dc(1)
を用いた実装ではありません。
このバージョンは単一プロセスであり、
プログラムをバイトコードに変換したものを解析して実行します。
「ドキュメントに記載されていない」オプション (-c) があり、
プログラムを実行する代わりに、それをバイトコードに変換した結果を
標準出力に出力します。
これは主として、パーザのデバッグと数学ライブラリの準備に用いられました。
主な相違点は拡張機能によるものです。
機能を高めたり追加したりするために機能が拡張されたり、
新機能が追加されたりしています。
相違点と拡張点のリストを以下に示します。
LANG
このバージョンは、
環境変数 LANG および LC_ で始まるすべての環境変数の処理に関して POSIX 標準に
準拠していません。
名前
伝統的な
bc
および POSIX
bc
は、関数、変数、配列の名前として単一の文字を使います。
このバージョンでは、
先頭が文字で始まり、文字と数字とアンダースコアで
構成される 2 文字以上の名前が使えるように拡張されています。
文字列
文字列には NUL 文字を含むことはできません。
POSIX では、文字列にはあらゆる文字を含めることができなければならない、
としています。
last
POSIX bc には変数 last はありません。
bc の実装によっては、last と同じ意味で
ピリオド (.) を用いるものがあります。
比較
POSIX bc では、比較は if 文、while 文、for 文の第 2 式の中でのみ
用いることができます。
また、これらの文の中ではただ 1 つの関係演算しか使えません。
if 文, else 節
POSIX bc には else 節はありません。
for 文
POSIX bc では for 文の各演算式は省略できません。
&&, ||, !
POSIX bc には論理演算子はありません。
read 関数
POSIX bc には read 関数はありません。
print 文
POSIX bc には print 文はありません。
continue 文
POSIX bc には continue 文はありません。
return 文
POSIX bc では、return 文の周りに括弧が必要です。
配列パラメータ
POSIX bc では (現在のところ) 配列パラメータは完全には使えません。
POSIX の文法では、関数定義では配列を使えますが、実際に呼び出すときの
パラメータに配列を指定することができません。(これはおそらく、文法上の
見落としでしょう。) 伝統的な bc の実装では、配列パラメータは値渡し
のみでした。
function format
POSIX bc では、開くブレースが define キーワードと同じ行にあり、
auto 文が次の行にあることが必要です。
=+, =-, =*, =/, =%, =^
POSIX bc ではこれらの「旧式」の代入演算子を定義する必要はありません。
このバージョンではこれらの「旧式」代入演算子が使えるかも知れません。
limits 文を使って、インストールしたバージョンがこれらをサポートしているか
どうか、確かめてみて下さい。
もしそのバージョンが「旧式」代入演算子をサポートしていれば、
文 a =- 1 は a に値 -1 を代入する代わりに a を 1 減じます。
数字表記中の空白
他の bc 実装では、数字表記の中に空白を含めることが許されます。
例えば、x=1 3 は変数 x に値 13 を代入します。
このバージョンの bc では、先の文は文法エラーになります。
エラーと実行
このバージョンの bc は、
プログラムに文法上のエラーや他のエラーが見つかった場合に
どういうコードが実行されるか、
という点で、他の実装と異なっています。
ある関数定義中で文法エラーが見つかると、
エラー回復機構は文の先頭を見つけて関数のパーズを続けようと努力します。
ひとたび関数の中で文法エラーが見つかると、
その関数は呼び出せなくなり、未定義状態となります。
対話的実行コードで文法エラーがあると、
現在の実行ブロックが無効になります。
実行ブロックとは、ひと続きの完全な文のあとの行末までのことです。
例えば、次のコード
a = 1 b = 2には 2 つの実行ブロックがあり、
{ a = 1 b = 2 }には 1 つの実行ブロックがあります。 ランタイムエラーが発生すると、現在の実行ブロックの実行が終了します。 ランタイムの警告が発生しても、現在の実行ブロックは終了しません。 割り込み 対話セッションの間、SIGINT シグナル (通常、端末からの Control-C 入力で 発生します) によって現在の実行ブロックの実行が中断され、 どの関数が中断されたかを示す「ランタイム」エラーが表示されます。 ランタイムのデータ構造をすべてクリアした後メッセージが表示され、 bc は次の入力を受け付ける状態になったことを示します。 これまでに定義した関数はすべて定義されて残っており、 ローカルでない変数の値は割り込み発生時点の値のままになっています。 ローカル変数と関数パラメータはすべて、クリア処理によって消去されます。 非対話セッションでは、SIGINT シグナルで bc の実行全体が終了します。
Philip A. Nelson philnelson@acm.org