例25 係数a,b,cを入力すると2次方程式ax2+bx+c=0の解を表示するプログラム
10 INPUT a,b,c 20 LET D=b^2-4*a*c 30 IF D>=0 THEN 40 PRINT (-b-SQR(D))/(2*a), (-b+SQR(D))/(2*a) 50 ELSE 60 PRINT "解なし" 70 END IF 80 END
このプログラムは,判別式D=b2-4ac の値によって処理を分けています。
D≧0のときには解の公式を用いて2つの解を計算して表示し,そうでないときには,「解なし」と表示します。
不等号「≧」は,BASICでは「>=」で代用します。
このように,ある条件が成立するときは〜を実行し,そうでないとき〜を実行する処理を記述するのに
IF
〜 ELSE 〜 END IFを用います。この構文は,次の形式で記述します。
IF 条件 THEN〜の部分には複数の文を書くことができます。FOR〜NEXT構文やIF〜END IF構文のように複数行で構成される命令を書くこともできます。
〜
ELSE
〜
END IF
例25ではD=0のとき解が2個表示されてしまいます。D=0のときの処理を別扱いにするためには,たとえば,
100 INPUT a,b,c 110 LET D=b^2-4*a*c 120 IF D>0 THEN 130 PRINT (-b-SQR(D))/(2*a), (-b+SQR(D))/(2*a) 140 ELSE 150 IF D=0 THEN 160 PRINT -b/(2*a) 170 ELSE 180 PRINT "解なし" 190 END IF 200 END IF 210 END
のようにすればよいのですが,これと同じことを次のように書くことができます。
例26
100 INPUT a,b,c 110 LET D=b^2-4*a*c 120 IF D>0 THEN 130 PRINT (-b-SQR(D))/(2*a), (-b+SQR(D))/(2*a) 140 ELSEIF D=0 THEN 150 PRINT -b/(2*a) 160 ELSE 170 PRINT "解なし" 180 END IF 190 END
例25のプログラムを
10 INPUT a,b,c 20 LET D=b^2-4*a*c 30 IF D>=0 THEN PRINT (-b-SQR(D))/(2*a), (-b+SQR(D))/(2*a) ELSE PRINT "解なし" 40 END
と書くことができます。このように,条件が成立するとき,成立しないときに実行する文がどちらも一つであり,それらがともに単純実行文と呼ばれる種類の文である場合には,
IF 条件 THEN 単純実行文 ELSE 単純実行文
の形のIF文が利用できます。LET文,INPUT文,PRINT文,SET文,PLOT文,RANDOMIZE文などは単純実行文です。しかし,IF文は単純実行文ではありません。
なお,条件が成立しないときに実行する文がない場合には,
IF 条件 THEN 単純実行文
の形のIF文が使えます。たとえば,D<0のとき「解なし」と表示する必要がなければ,
10 INPUT a,b,c 20 LET D=b^2-4*a*c 30 IF D>=0 THEN PRINT (-b-SQR(D))/(2*a), (-b+SQR(D))/(2*a) 40 END
とします。
不等式 a≦b,a≧bは,それぞれ,a<=b,a>=bのように書きます。また,aとbとが等しくないこと(a≠b)は,a<>bと書きます。
AND,OR,NOTと括弧を用いて複雑な条件を記述することができます。
たとえば,「p かつ q」という条件をBASICでは p AND q と書きます。また,「p
またはq」をBASICでは p OR qと書きます。
否定を表すNOTは,否定対象の直前に書きます。AND,OR,NOTが混じった条件を書いたときには,NOTが最も優先します。ANDとORとではANDが優先します。優先順位を変更したいときは括弧を用いることができます。
なお,数学では「a<x かつ x<b」のことをa<x<b と書きますが,BASICではこういう書き方はできません。
例 27 3または8で割り切れる3桁の自然数の総和を求めるプログラム
10 LET t=0 20 FOR n=100 TO 999 30 IF MOD(n,3)=0 OR MOD(n,8)=0 THEN 40 LET t=t+n 50 END IF 60 NEXT n 70 PRINT t 80 END
x2+y2=z2となる3整数x,y,zをピタゴラス数といいます。ピタゴラス数を系統的に探すのには,x,yの値を順に変化させてz
= SQR(x^2+y^2) が整数となるときのx,y,zを出力するようなプログラムを作ればいいでしょう。zが整数であるかどうかは,INT(z)がzと一致するかどうか調べればわかります。次のプログラムは,1≦x≦y≦100の範囲でピタゴラス数を探します。
例28
10 FOR x=1 TO 100 20 FOR y=x TO 100 30 LET z=SQR(x^2+y^2) 40 IF INT(z)=z THEN PRINT x,y,z 50 NEXT y 60 NEXT x 70 END
[Note] 上のプログラムは,N88-BASICのような古いタイプのBASICでも動作するでしょう。しかし,正しい結果は得られないかも知れません。JIS Full BASICでは,べき乗や平方根などの計算で,真の値が有効数字の桁数以下の十進数で表現できる場合には真の値が得られることになっているので,上のプログラムで正しい結果が得られます。
DO文とLOOP文は対にして用いられ,繰り返しの処理を記述するのに用いられます。
例29 無限ループ
10 LET A=0 20 DO 30 LET A=A+1 40 PRINT A 50 LOOP 60 END
DO〜LOOPの構文を書くと,DO文とLOOPの間に書かれた文が繰り返し実行されます。上の例では30行と40行が繰り返し実行されます。
[補足] 上のプログラムを実行したときは, をクリックするか,実行メニューから中断を選んで実行を打ち切ってください。
条件が成立する間,繰り返しを実行する構文です。
例30
10 LET A=0 20 DO WHILE A<100 30 LET A=A+1 40 PRINT A 50 LOOP 60 END
例29の20行を修正して上のようにすると,Aの値が100になると繰り返しをやめるようになります。
DO WHILE 〜 LOOPは次の形式で書きます。この構文の動作を流れ図で表すと図のようになります。
DO WHILE 条件LOOP
この構文では繰り返すべき処理を実行する前に条件を調べますから,最初から条件が成立しない場合には,1回も繰り返しを実行しません。たとえば,例30
で20行を
20 DO WHILE A<0
に変えると30行,40行は1回も実行されません。
DO WHILE 〜 LOOPは条件が成立する間,繰り返しを実行する構文ですが,DO UNTIL
〜 LOOPは,繰り返しを終了する条件を指定して繰り返しを実行させる構文です。
例31
10 LET A=0 20 DO UNTIL A>=100 30 LET A=A+1 40 PRINT A 50 LOOP 60 END
例31は例30とまったく同じ意味です。一般に,条件の否定を利用すれば,DO WHILE 〜 LOOPとDO UNTIL 〜 LOOPは相互に書き換えが可能です。UNTIL p はWHILE NOT pと同じです。どちらを用いるかは,プログラムを書く人がどちらを向いて思考しているかの相違によります。
DO〜LOOPを利用して入力された自然数を素因数分解するプログラムを作ることができます。変数nに分解すべき自然数を入力するものとします。まず,最初にnが2で割れるかどうか調べ,割れるときは2を出力してnを2で割ったもので置き換えます。これをnが2で割り切れる間,繰り返し実行します。nが2で割れなくなったら割る数を3にして同様のことを繰り返します。次は割る数を5にして同様のことを繰り返せばよいのですが,「次の素数」をコンピュータに答えさせるのは難しいので,割る数は単に1を加算していくだけにします。すると,割る数3の次は割る数4になるのですが,それでも不都合はありません。なぜかというと,すでに2で割れるだけ割ってあるからです。
例32 素因数分解
110 INPUT n 120 LET f=2 130 DO UNTIL n=1 140 DO WHILE MOD(n,f)=0 150 PRINT f; 160 LET n=n/f 170 LOOP 180 LET f=f+1 190 LOOP 200 END
変数fは割る数(因数,factor)を表します。120行で最初の割る数2をセットしています。140行から170行までの繰り返しでfで割れる間,割ります。割れなくなると(180行),fは1加算されます。これを130行のDO文と190行のLOOP文とで繰り返すのですが,いつか繰り返しを止めなければなりません。その条件は,nからすべての因数を取り尽くしたときですから,n=1になります。それが,130行に書かれた条件です。
EXIT DO文は,DO〜LOOPの繰り返しを終了してLOOP文の次の行に制御を移す命令です。DO
WHILE 〜 LOOPやDO UNTIL 〜 LOOPは繰り返しの中身を実行する前にそれを実行すべきかどうか判断します。しかし,その位置では繰り返しを継続すべきかどうかの判断ができない場合があります。そのような場合,EXIT
DO文を用います。
次のプログラムは,正しい答えを入力するまで繰り返し入力を要求します。この場合,入力してからでないと正しいかどうかの判断のしようがありません。
例33
10 PRINT "2×3=?" 20 DO 30 INPUT n 40 IF n=2*3 THEN EXIT DO 50 PRINT "残念,もう一度" 60 LOOP 70 PRINT "正解!" 80 END
例33の処理の流れを図に表すと次のようになります。
正の整数nを入力するとnを2進法で表示するプログラムを作ります。
たとえば,n =11とすると
11=1×23+0×22+1×2+1
ですから,11を2進法で表すと1011です。これを下位の桁から順に求めたいときは,次のようにします。
2)11 ・・・1
2) 5 ・・・1
2) 2 ・・・0
2) 1 ・・・1
0
整数aを整数bで割ったときの商をINT(a/b),余りをMOD(a,b)で求められるので,次のプログラムが得られます。
例34
10 INPUT n 20 DO 30 LET q=INT(n/2) 40 LET r=MOD(n,2) 50 PRINT r 60 IF q=0 THEN EXIT DO 70 LET n=q 80 LOOP 90 END
2つの正の整数a,bに対して,a,bの最大公約数をgcd(a,b)で表すことにすると,次の性質が成り立ちます。
aをbで割ったときの余りをrとするとき,
r = 0のとき,gcd(a,b) = b
r≠0のとき,gcd(a,b) = gcd(b,r)
この性質を繰り返し利用し,次のようにすると,2つの整数の最大公約数を求めることができます。これをユークリッドの互除法といいます。
aをbで割り,余りをrとする。
r=0であれば,bが最大公約数である。
r>0であれば,b,rを新たなa,bとしてこの操作を再度実行する。
この操作は,繰り返しのたびにbの値が小さくなっていくので,何回かの繰り返しの後,必ず終了します。
例35 ユークリッドの互除法により最大公約数を求める
10 INPUT a,b 20 DO 30 LET r=MOD(a,b) 40 IF r=0 THEN EXIT DO 50 LET a=b 60 LET b=r 70 LOOP 80 PRINT b 90 END
繰り返し処理の最後のところで,b,rの値を次のa,bの値とするために50行,60行に示すような順に代入文を用いることに注意してください。
プログラムのはじめに
DIM A(10)
と書くと,A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9),A(10)の計10個の変数が用意されます。A(1),A(2),A(3),…,A(10)は,それぞれ,普通の変数として使えます。このとき,この10個の変数をまとめて配列といって,個々の変数は添字付き変数といいます。そして,括弧内を添字(そえじ)といいます。
10 DIM A(10) 20 LET A(4)=15 30 PRINT A(4) 40 END
DIM文では,配列名と添字の上限を上のような形に書きます。配列名の付け方は普通の変数と同じです。添字の上限には10,20のような定数のみを書くことができます。
[Note]
配列の扱いは旧規格のJIS 基本BASICから変更されています。特に,配列の添字が1から始まることに注意してください。
添字付き変数では,添字に変数を含む式を書くことができます。たとえば,
10 DIM B(4) 20 LET i=3 30 LET B(i)=17 40 PRINT B(i) 50 END
この機能を利用すると,処理の対象となる変数をプログラムのなかで選ぶことが可能になります。BASICでは,B1,B2,B3のような変数名を使うこともできます。しかし,この場合には1,
2, 3の数字の部分は変数名の一部になっていますから,プログラムで番号を指定してB1,B2,
B3のうちのいずれかを選ぶことはできません。
例 36 フィボナッチ数列
フィボナッチ数列 { fn } は,
f1=1, f2=1, fn = fn-1+fn-2
(n = 3, 4, 5, …)
で定義される数列です。配列を利用してフィボナッチ数列のはじめの20項を求めるプログラムを書くと,次のようになります。
100 DIM f(20) 110 LET f(1)=1 120 LET f(2)=1 130 FOR i=3 TO 20 140 LET f(i)=f(i-1)+f(i-2) 150 NEXT i 160 FOR i=1 TO 20 170 PRINT i,f(i) 180 NEXT i 190 END
配列は分布表を作るのに利用できます。ここでは,例として100点満点の試験の結果を10点刻みで分布を調べるプログラムを作成します。 m(0)からm(10)までの11個の添字付き変数を用意し,0点から9点までの人数をm(0),10点から19点までの人数をm(1),20点から29点までの人数をm(2),…,で表すことにします。 m(0)からm(10)での11個の添字付き変数が使えるようにするために
DIM m(0 TO 10)
のようなDIM文をプログラムのはじめに書きます。 そして,
MAT m=ZER
を実行し,配列mの全要素に0(zero)を代入しておきます。数値を入力すると,どのランクに属するかを表す指標iを計算し,その指標が示す添字付き変数に1を加算します。
すべての数値が入力されたら,負の数を入力することにします。
[Note] MATは,matrix(行列)のはじめの3文字をとったもので,行列演算命令の意味です。ZERはzeroのはじめの3文字です。
例37
100 DIM m(0 TO 10) 110 MAT m=ZER 120 DO 130 INPUT n 140 IF n<0 THEN EXIT DO 150 LET i=INT(n/10) 160 LET m(i)=m(i)+1 170 LOOP 180 FOR i=0 TO 10 190 PRINT i*10;"〜";i*10+9,m(i) 200 NEXT i 210 END
例38 10個のさいころを振るときの目の数の和を求めるシミュレーションを10000回繰り返し,その結果を集計してヒストグラムに表示するプログラム。
100 DIM A(10 TO 60) 110 MAT A=ZER 120 FOR k=1 TO 10000 130 LET t=0 140 FOR n=1 TO 10 150 LET t=t+INT(RND*6+1) 160 NEXT n 170 LET A(t)=A(t)+1 180 NEXT k 190 SET WINDOW 9,61,0,1000 200 FOR t=10 TO 60 210 PLOT LINES: t-0.5,A(t); t+0.5,A(t); 220 NEXT t 230 END
目の数の和は10以上60以下になるので,添字の範囲を10から60までにした配列Aを用意し,度数分布を求めています。
整数の集合から合成数を順序よく取り除き,素数のみを残す方法で素数の集合を得ることができます。この手法は「エラトステネスのふるい」として古くから知られたものです。
2以上の整数を順に書き並べた表を用意し,n=2,3,4,…について,nの倍数であるような合成数n2,
(n+1)n, (n+2)n, … に印をつけると,最後に残るのは素数です。
2から1000までの添字のついた配列sを用意し,添字付き変数s(n)で整数nを表します。そして,整数nに印がついていることをs(n)=1,印がついていないことをs(n)=0で表現します。
例 39
100 DIM s(2 TO 1000) 110 MAT s=ZER 120 FOR n=2 TO 1000 130 IF s(n)=0 THEN 140 PRINT n 150 FOR k=n^2 TO 1000 STEP n 160 LET s(k)=1 170 NEXT k 180 END IF 190 NEXT n 200 END
nより小さいすべてのk(ただし2以上)について,kの倍数すべてに印をつける処理が終わっていれば,nに印がないことからnは素数であるという判断が下せます。この性質を利用して手順を合理化し,120〜190行のFOR〜NEXTループですべてを処理しています。
150〜170行のFOR〜NEXTでnの倍数に印をつけています。kの値をn2から順にnずつ増加させていくのではkの値が1000にならないことがありますが,FOR〜NEXT構文の性質からkの値が1000を超えたとき繰り返しを終了するのでこれで問題なく動作します。
配列には2次元,3次元のものも使えます。たとえば,DIM A(2,3) と書くと,A(1,1),A(1,2),A(1,3),A(2,1),A(2,2),A(2,3)の6個の添字付き変数が用意されます。
DIM文では,複数の配列をまとめて宣言することができます。その場合,各配列宣言はコンマで区切って書きます。たとえば,
DIM A(4),B(10),C(20)
のように書きます。
Full BASICでは,配列の添字は1から始まるものと考えています。配列の添字が0から始まるようにしたいときは,最初の配列宣言を書く行よりも上の行にOPTION
BASE 0と書きます。すると,すべての配列で添字の下限が0になります。
10 OPTION BASE 0 20 DIM A(4) 30 LET A(0)=7 40 MAT PRINT A 50 END
添字の下限を1または0以外の値にしたい場合や,配列ごとに異なる値にしたい場合には次の例に示すような構文を用いることもできます。
DIM A(-4 TO 5),B(0 TO 2, 1 TO 3)
この配列宣言でA(-4)からA(5)までの10個の添字付き変数と,B(0,1)からB(2,3)までの9の添字付き変数が用意されます。
2次元配列を利用してパスカルの三角形,すなわち,組合せの数nCrの表を作ってみましょう。nCrは,次の漸化式を用いて計算することができます。
r = 0 または r = n のとき nCr=1
0<r<n のとき nCr = n-1Cr-1+n-1Cr
例40
100 DIM C(1 TO 10, 0 TO 10) 110 MAT C=ZER 120 FOR n=1 TO 10 130 FOR r=0 TO n 140 IF r=0 OR r=n THEN 150 LET C(n,r)=1 160 ELSE 170 LET C(n,r)=C(n-1,r-1)+C(n-1,r) 180 END IF 190 NEXT r 200 NEXT n 210 MAT PRINT C; 220 END
BASICは配列を処理するための便利な機能を用意しています。たとえば,MAT INPUT文とMAT
PRINT文を用いると配列入出力が簡単になります。
例41
10 DIM A(5) 20 MAT INPUT A 30 MAT PRINT A 40 END
MAT INPUT文は,配列に数値をまとめて入力します。20行のMAT INPUT文の実行時には5個の数値をコンマで区切って入力してください。MAT
PRINT文は,配列に属するすべての添字付き変数の値をまとめて出力します。なお,
30 MAT PRINT A;
のように配列名に続けてセミコロンを書いておくと,数値が詰めて出力されます。
MAT READ文はDATA文から配列に数値を代入します。 DATA文には必要な個数だけの数値をコンマで区切って書いておきます。テスト段階のプログラムなどで毎回同じデータを入力したいとき便利です。
例42 最小値を求める
10 DIM A(10) 20 DATA 45,61,73,91,43,12,65,34,96,12 30 MAT READ A 40 LET k=1 50 FOR i=2 TO 10 60 IF a(i)<a(k) THEN LET k=i 70 NEXT i 80 PRINT a(k) 90 END
10個の数値を入力すると大きさの順に並べ替えて出力するプログラムを作ります。
10個の数値はa(1)からa(10)までの10個の添字付き変数に入力するものとします。まず,a(1)からa(10)までの数値のうちで最小のものをa(1)と交換します。すると,a(1)が一番小さい数になります。次に,a(2)からa(10)までのうちで最小のものをa(2)と交換します。すると,a(2)に2番目に小さい数が入ります。以下,同様に,
FOR i=1 TO 9
a(i)からa(10)までのうちで最小のものをa(i)と交換する
NEXT i
を実行すれば,配列aには数値が小さい順に並べ替えられて入ることになります。
例43
100 DIM a(10) 110 MAT INPUT a 120 FOR i=1 TO 9 130 LET k=i ! kは最小値の番号 140 FOR j=i+1 TO 10 150 IF a(j)<a(k) THEN LET k=j 160 NEXT j 170 LET t=a(i) ! tを作業用の変数として用いて 180 LET a(i)=a(k) ! a(k)とa(i)の値を交換する 190 LET a(k)=t 200 NEXT i 210 MAT PRINT a; 220 END
130〜160行はa(i)〜a(10)の最小値の番号を変数kに取得する処理です。170〜190行ではtを作業用の変数として利用してa(i)の値とa(k)の値とを交換しています。
! 以降は行末注釈です。プログラムの実行には影響しません。プログラムの意味を説明したいときなどに利用します。
なお,このプログラムがどのように動いているのか見たいときは,210行のMAT
PRINT文を適当な位置に移してみるとよいでしょう。
再帰的な発想法を用いると,少し異なった種類の整列アルゴリズムを作ることができます。a(1)〜a(i-1)がすでに大きさの順に並んでいるとしたら,a(i)をしかるべき位置に挿入して以後の数値を順に後ろにずらしてやればa(1)〜a(i)が大きさの順に並びます。そこで,i=2からi=10まで順に上に述べた処理を繰り返せば大きさの順に並ぶことになります。
例44
100 DIM a(10) 110 MAT INPUT a 120 FOR i=2 TO 10 130 LET b=a(i) ! a(i)をbに退避する 140 LET j=i ! bを挿入すべき位置をjに求める 150 DO WHILE j>1 AND a(j-1)>b 160 LET a(j)=a(j-1) ! データを後方にずらす処理も同時に行う 170 LET j=j-1 180 LOOP 190 LET a(j)=b 200 NEXT i 210 MAT PRINT a; 220 END
130〜180行でa(i)の値を挿入すべき位置を探しています。そのとき,同時に各変数の値を後ろにずらしています。
JIS Full BASICのANDとORには特徴的な性質があります。p AND qでは,pが偽であるとわかったときにはqの真偽を調べません。また,p
OR qでは,pが真であるとわかったときにはqの真偽を調べません。150行のAND演算では,j>1とa(j-1)>bを調べる順序が重要です。150行ではj=1になることがありますから,j>1よりも先にa(j-1)>bを調べると添字が範囲外となってエラーになってしまいます。
PRINTをPLINTと綴ったり,FORに対応するNEXTがなかったりするとコンパイラはエラーを報告します。これは文法上の誤りです。
xの値が0であるときに1/x を計算すれば当然エラーになります。また,BASICには扱いうる最大の数が定められていて,計算結果の絶対値がその値(MAXNUM)を超える場合にもエラーになります(桁あふれ)。このように実行時に起こるエラーを特に例外と呼んで,文法上の誤りと区別します。
ゼロ除算のエラーならばあらかじめ除数が0であるかどうか調べておけば防げますが,桁あふれのエラーを予測するのは実質的に不可能です。このような場合にはエラーが起きてから対処するしか方法がありません。
WHEN EXCEPTION構文は,実行時エラー(例外)が発生したときの処理を記述する構文です。次の形に書きます。
WHEN EXCEPTION IN 例外を起こす可能性のある処理 USE 例外が起きたとき実行すべき処理 END WHEN
WHEN EXCEPTION IN行とUSE行の間に書かれた文で例外が起こると,その文の実行を中断してUSE行の次の行に制御を移します。一方,WHEN
EXCEPTION IN行とUSE行の間に書かれた文が例外を発生することなく実行されたときは,USE行とEND
WHEN行の間に書かれた文は実行されません。
この構文は関数のグラフを描く場合には不可欠なものといえます。
例46 関数y = tan x のグラフ
100 OPTION ANGLE DEGREES 110 DEF f(x)=TAN(x) 120 SET WINDOW -180,180,-4,4 130 DRAW AXES(90,1) 140 FOR x=-180 TO 180 150 WHEN EXCEPTION IN 160 PLOT LINES: x,f(x); 170 USE 180 PLOT LINES 190 END WHEN 200 NEXT x 210 END
なお,180行のPLOT LINESがないと,たとえば,2点(89,TAN(89)),(91,TAN(91))を結ぶ線分が描かれてしまいます。