The Office Uchida, School of Computer

コンピュータを学習する人の学校:パソコンキャンパス、プログラミングキャンパス
ホームCプログラマ徹底養成コース上級コース 第12問
文字の拡大

上級コース 第12問:任意桁BASICインタープリタ

問題:簡単なBASICインタープリタを作成して下さい。このBASICインタープリタをBigBasicと呼びましょう。 ただし、BigBasicは、問11で作成した任意桁整数計算ライブラリを ベースにして任意桁実数計算ライブラリを作成し、それを用いてsinや平方根の計算も任意桁で行えるようにして下さい。 ただ、実数計算はかなり数学的な話題を含んでしまうので、この任意桁実数計算ライブラリは、第1ヒントの段階ですべての ソースリストを公開します。時間のない方は、任意桁実数計算ライブラリは与えられるものとして、問題12を解いていただいても結構です。

サンプルプログラム

まず、BASICインタープリタのイメージを明確にしていただくために、簡単なサンプルプログラムとその実行結果を示しましょう。 次のプログラムは、2つのデータを入力し、その積を計算し、その結果を表示します。 このプログラム例から分かるように、変数は宣言しないで使います。変数に型はなく、すべて任意桁実数です。 ただし、使える変数は、aからzまで26個の変数だけです。

青い太字で示されている字句はキーワードです。キーワードは小文字で記述します。

input文は、以下のサンプルに示すように、最初に文字列を記述できます。 すると、入力の際に、その文字列がプロンプトのように表示され、その後にデータを入力することができます。

計算式は、C言語と同じように記述できますが、四則演算(+, -, *, /)だけです。整数計算はありません。すべて 実数計算になります。

print文によって、指定された文字列や数値が表示されます。文字列や数値はコンマで区切ります。

1行には1つの命令だけを書いてください。1行に複数の命令は書けません。

Sample list  b1.bas
input "x = ", x
input "y = ", y
z = x * y
print "z = ", z

このプログラムを実行すると、次のようになります。

% BigBasic b1.bas
x = 123456789012345678901234567890
y = 12345678901234567890
z = 1524157875323883675034293577501905199875019052100

任意桁なので、上記のように桁数に制限なく、何桁でも計算させることができます。

次の例は階乗を求めるプログラムです。for文は、「for 変数 = 初期値 to 最終値 [step 増分]」の形式をしています。step以下は 省略できます。ただし、このfor文は変数を増加させることしかできませんので、ご注意ください。すなわち、「増分」の部分には正の値しか 指定できません。

Sample list  b2.bas
print "1から30までの階乗"
f = 1
for k=1 to 30
	f = f * k
	print k,"!=",f
next k

このプログラムを実行すると、次のようになります。

% BigBasic b2.bas
1から30までの階乗
1!=1
2!=2
3!=6
4!=24
5!=120
6!=720
7!=5040
8!=40320
9!=362880
10!=3628800
11!=39916800
12!=479001600
13!=6227020800
14!=87178291200
15!=1307674368000
16!=20922789888000
17!=355687428096000
18!=6402373705728000
19!=121645100408832000
20!=2432902008176640000
21!=51090942171709440000
22!=1124000727777607680000
23!=25852016738884976640000
24!=620448401733239439360000
25!=15511210043330985984000000
26!=403291461126605635584000000
27!=10888869450418352160768000000
28!=304888344611713860501504000000
29!=8841761993739701954543616000000
30!=265252859812191058636308480000000

さらに、次の例は、2次方程式を解くプログラムです。上級編 問題1に 桁落ちのため、「解の公式」をそのまま使うといけないので「工夫すべし」という問題がありましたが、 このBASICは任意桁ですので、そのまま計算してみましょう。

Sample list  b3.bas
rem 2次方程式の計算

print "桁落ちの発生しない(?)2次方程式"
a = 1
b = 1234567
c = 0.01

if a == 0 then goto Error

x = ( -b + sqrt( b*b - 4*a*c ) ) / ( 2*a )
y = ( -b - sqrt( b*b - 4*a*c ) ) / ( 2*a )

if x == y then print "重解です"

print "x1=", x
print "x2=", y

goto FIN
label Error
print "係数aがゼロなので実行できない"
label FIN
print "プログラムの終了"

1行目のremの後にはコメントを書くことができます。

if文は、「if 式1 関係演算子 式2 then 文」の形式をしています。この例では、文の部分にはprint文とgoto文が指定されています。 なお、C言語にあるようなブロックはありません。

goto文は、「goto ラベル」の形式をしています。ラベルの定義場所に実行を移します。ラベルは、label文によって定義します。

label文は、「label ラベル」の形式をしています。この文が記述された場所がラベルの定義域になります。

計算式の中には、組込み関数を記述することもできます。sqrtは平方根を求める組込み関数です。

実行結果は次のようになります。正確に、答えが求められています。ただし、これによって桁落ちの問題が解決したわけではありません。 この結果に対する評価の分析は慎重に行う必要があります。

% BigBasic b3.bas
桁落ちの発生しない(?)2次方程式
x1=-0.0000000081000059130043696373674257498797116945842137032419732364611192797678766887772040059598090162
x2=-1234566.9999999918999940869956303626325742501202883054157862967580267635388807202321233112227959940401909838
プログラムの終了

BigBasicの最後のサンプルプログラムの例として、サインカーブを描くプログラムを紹介しましょう。

Sample list  b4.bas
print "sin curve"
p = 3.1415926535
for x = -p to p step 0.3
	t = 10*(sin(x)+1.0)
	print tab(t),"*"
next x

tabという組込み関数がありますが、これはprint文の中だけで使える組込み関数で引数に指定された数だけ空白を表示するものです。

このプログラムを実行すると、次のようになります。

% BigBasic b4.bas
sin curve
         *
       *
    *
  *
*
*
*
 *
   *
     *
        *
           *
              *
                *
                  *
                   *
                   *
                   *
                 *
               *
            *

BigBasicの文法仕様

BigBasicの文法仕様は、次のようになります。

BigBasicの文法仕様
変数任意桁実数。ただし、除算、組込み関数などでは、小数点以下50桁まで求める。
変数名英小文字1文字のみ。つまり、a, b, c, ..., x, y, zの26個のみ。
定数10進浮動小数点定数。桁数の制限なし。指数の指定はできない。浮動小数点は省略可能。
演算子として、加算(+)、減算(-)、乗算(*)、除算(/)、符号(+,-)が可能。 演算子の優先順位は、符号>乗算、除算>加算、減算の順である。括弧によって優先順位を変更できる。別途定義する組込み関数が記述できる。
組込み関数sin(x):正弦関数、cos(x):余弦関数、sqrt(x):平方根関数、log(x):対数関数、exp(x):指数関数、 pow(x,a):べき乗(xのa乗)、tab(x):x個空白を表示(print文の中でのみ利用可能)。各組込み関数は、小数点以下50桁まで計算する。
関係演算子if文の中でのみ利用可能。演算子として、等号(==)、より大きい(>)、以上(>=)、より小さい(<)、以下(<=)、 等しくない(!=)がある。
入力文一般形式 input obj [, obj ]...
   objは、文字列あるいは変数名。
意味:標準入力から数値データを入力して変数に格納する。文字列がある場合には、それを画面に表示して、改行なしに次のデータの入力に移る。
例: input "a=",a,"b=",b,"c=",c
実行結果:以下のようになる。赤が人間が入力した部分。それぞれの値が各変数にセットされる。入力するデータは定数のみ。文字データは入力できない。
a=123
b=456
c=789
出力文一般形式 print obj [, obj ]...
   objは、文字列あるいは変数名あるいはtab関数。
意味:標準出力に文字列あるいは数値データを出力し、最後に改行する。tab関数は、tab(x)という形式で、xは定数あるいは変数。その値だけ空白を表示する。
例: print "a=",a,", b=",b,tab(2),", c=",c
実行結果:以下のようになる。赤が出力された部分である。
a=123, b=456,  c=789
for文一般形式 for 変数 = 式1 to 式2 [ step 式3 ]
          文1
          文2
          :
          文n
       next 変数
  式1、式2、式3は、式であり、式1≦式2、式3>0でなければならない。
意味:変数の値を、式1(初期値)から式2(最終値)まで式3(増分)ずつ増加させながら、 「next 変数」の間の文を上から順に実行させる。 ループを強制的に終了させる場合には、goto文を使う。逆にgoto文でループの内部に制御を移してはならない。
例: サンプルプログラムを参考にされたい。
条件文一般形式 if 式1 関係演算子 式2 then
   関係演算子は、関係演算子の項を参考にされたい。
意味:式1と式2の関係を関係演算子に従って評価し、その条件が成立すれば文を実行する。
例: if a == b then print "x=", x
例: if a == b then goto ERROR
goto文一般形式 goto ラベル
          文1
          文2
          :
          文n
       label ラベル
  ラベルは、英字で始まる単語。大文字でも小文字でもよいが、これらは区別される。
意味:「goto ラベル」を実行すると、同じ名前のラベルが定義されている「label ラベル」文に制御が移る。 goto文とラベル文の位置に制限はない。同じ名前のラベルを2つ以上定義してはいけない。
例: サンプルプログラムを参考にされたい。
注釈文一般形式 rem 任意のメッセージ
意味:この文は、プログラムの実行に何も影響を与えない。
例: rem これはコメントです

BigBasicのコマンド仕様

BigBasicを起動するコマンドの構文は次の通りです。

BigBasic basicファイル名

任意桁浮動小数点ライブラリ BigFloat

このBigBasicインタプリタを実現するためには、任意桁浮動小数点ライブラリ BigFloatを開発する必要があります。 しかし、多少数学的話題を含み、かつ、時間的な観点から、このライブラリは独自に開発しなくてもよいものとします。 第1ヒントですべてのソースプログラムを公開します。しかし、自分である程度開発してみたい方は、 任意桁浮動小数点ライブラリ BigFloatのヒントをご覧下さい。

ここには参考のために、ヘッダファイルを示します。

Sample list  BigFloat.h
#include"BigInteger.h"

typedef struct
{
	BigInteger *digit;
	int fl; /* 浮動小数点以下の数値の桁数 */
	int magicNumber; /* BigFloat型であることのマジックナンバー */
} BigFloat;

extern int bferror; /* エラー発生定数 */

BigFloat *BItoBigFloat( BigInteger *bg ); /* BigInteger型をBigFloat型に変換 */
BigFloat *InttoBigFloat( int n );         /* int型をBigFloat型に変換 */
BigFloat *toBigFloat( char *num );        /* 文字列をBigFloat型に変換 */
char *BFtoString( BigFloat *bf );         /* BigFloat型を文字列に変換 */
void freeBigFloat( BigFloat *b3 );        /* BigFloat型オブジェクトを削除 */

/* 四則演算 */
BigFloat *addBigFloat( BigFloat *bf1, BigFloat *bf2 );
BigFloat *subBigFloat( BigFloat *bf1, BigFloat *bf2 );
BigFloat *multBigFloat( BigFloat *bf1, BigFloat *bf2 );
BigFloat *divideBigFloat( BigFloat *b1, BigFloat *b2, int n );

/* 支援関数 */
BigFloat *copyBigFloat( BigFloat *b1 ); /* コピー関数 */
BigFloat *reverseBigFloat( BigFloat *b1 ); /* 符号反転 */
int compareBigFloat( BigFloat *b1, BigFloat *b2 ); /* 比較関数 */
int zeroCount( BigFloat *bf ); /* 小数点以下のゼロの桁数 1.00000023なら6を返す */
BigFloat *roundOff( BigFloat *bf, int n ); /* 四捨五入関数 小数点以下n桁で丸める */

/* 組込み関数の計算 */
BigFloat *sqrtBigFloat( BigFloat *a, int n );
BigFloat *cosBigFloat( BigFloat *x, int n );
BigFloat *sinBigFloat( BigFloat *x, int n );
BigFloat *expBigFloat( BigFloat *x, int n );
BigFloat *logBigFloat( BigFloat *x, int n );
BigFloat *powBigFloat( BigFloat *x, BigFloat *p, int n );

学習ポイント
  1. 文字列処理、言語処理のプログラムに慣れる
  2. デバッガの使い方に慣れる
  3. プログラム設計において図を正しく書くことの大切さについて実感する

本問題に対するFAQ

質問1:いったいどこから手をつければよいのでしょうか。

 まず、BigFloatライブラリを完全に作成する必要があります。ただ、このプログラムは 時間の関係と数学的な問題を多少含むので第1ヒントで与えられます。なお、BigFloatライブラリを 自作する場合、極力、BigIntegerライブラリを利用し、なるべく新しいプログラムを作らないようにしましょう。 Basicインタープリタを開発する場合、それなりのプログラム作成法がありますので、言語処理プログラムを 書いたことのない方は第1ヒントをご覧になってから手をつけた方がよいでしょう。

質問2:学習ポイントに「デバッガの使い方に慣れる」とありますが、これはどのようなことですか。

 言語処理プログラムの中でもインタープリタは、比較的易しい部類に属しますが、 それでも結構面倒な部分もあります。プログラムが想定した通りに動かないことはよくあります。 そのような場合、デバッガを使うと効果的です。
 ただし、色々な環境があり、それぞれ異なるデバッガを使っている方も多いと思います。 ここでは、第2ヒントでCygwinのgdbについて簡単にふれますが、それぞれお使いのデバッガについては しっかりと使いこなせるようにして下さい。

質問3:このBigBasicインタープリタのエラーメッセージは、どこまでチェックすればよいのでしょうか。

 理想を言えば徹底的にチェックしてエラーメッセージを表示すればよいのですが、開発期間、コスト、実行効率の点から 総合的に判断すべきです。今回のインタープリタは、実用的に使うことを想定しておりませんが、もし、実用的に使うのであれば、 かなり徹底的にチェックすべきでしょう。特に、誤動作を誘発するようなエラーを見過ごしてはいけません。言語処理系の80%以上は、エラーチェックという 報告もあります。