4 プログラム分割

4.1 外部関数定義

4.1.1 プログラム分割の必要性

例28を実行すると,6,8,10のような無意味なピタゴラス数も出力します。
これを防ぐためにはx,yの最大公約数が1でないものを除外すればいいのですから,
例28のプログラム中に最大公約数を求めるプログラム例35を埋め込んで

100 FOR x=1 TO 100
110    FOR y=x TO 100
120       LET a=x
130       LET b=y
140       DO
150          LET r=MOD(a,b)
160          IF r=0 THEN EXIT DO
170          LET a=b
180          LET b=r
190       LOOP
200       IF b=1 THEN
210          LET z=SQR(x^2+y^2)
220          IF INT(z)=z THEN PRINT x,y,z
230       END IF
240    NEXT y
250 NEXT x
260 END 

とすればいいでしょう。しかし,このようなプログラムを作るのは,実際には容易ではありません。たとえば,例28のプログラムが変数名としてx,y,zでなくa,b,cを用いていたとしたら,合体させたプログラムは正常に動作しません。

4.1.2 外部関数定義

BASICには多くの関数が組込み関数として用意されていますが,それで十分とはいえません。DEF文は利用者が関数を定義する機能を提供していますが,DEF文では数値式で書けるような関数しか定義できません。BASICには,より複雑な処理をしなければ求められないような関数でも定義できる機能が用意されています。
前項に示したプログラムは,2数a,bの最大公約数をGCD(a,b)と書くことにすればもっとわかりやすく書くことができます。そのために外部関数定義というものを使います。
BASICのプログラムではEND行までの部分を主プログラムといいます。そして,END行以降には外部手続きと呼ばれるものが続きます。外部関数定義も外部手続きの一種です。
例47は,200行から280行までの部分がGCD関数を定義する外部関数定義です。外部関数定義部はEXTERNAL FUNCTION行で始まり,END FUNCTION行で終わります。EXTERNAL FUNCTION行には,関数名と引数(ひきすう)を書きます。引数は括弧でくくって書きます。引数が2個以上ある場合にはそれらをコンマで区切って書きます。

例47

100 DECLARE EXTERNAL FUNCTION GCD
110 FOR x=1 TO 100
120    FOR y=x TO 100
130       IF GCD(x,y)=1 THEN
140          LET z=SQR(x^2+y^2)
150          IF INT(z)=z THEN PRINT x,y,z
160       END IF
170    NEXT y
180 NEXT x
190 END
200 EXTERNAL FUNCTION GCD(a,b)
210 DO
220    LET r=MOD(a,b)
230    IF r=0 THEN EXIT DO
240    LET a=b
250    LET b=r
260 LOOP
270 LET GCD=b
280 END FUNCTION 

主プログラムと外部手続きはプログラム単位と呼ばれます。外部関数を利用するプログラム単位には,その外部関数の名前を関数名として用いることの宣言文を書きます。上のプログラムでは,主プログラムで外部関数としてGCDを用いるために100行のような宣言文を書いています。このような宣言を書くことで,外部関数の名前と同じ組込み関数があったとしても正しく動作させることが可能となります。
宣言文は,実際に関数を使う行よりも上の行に書きます。文法的には,DECLARE EXTERNAL FUNCTION に続けて目的の関数名を書くだけです。

[Note]
(仮称)十進BASICの現バージョンでは組込み関数名と一致しない外部関数を用いる場合には宣言文を省略できますが,将来のバージョンでは組込み関数を増やすかも知れないので,宣言文は必ず書いてください。また,将来のバージョンでは外部関数宣言の省略を許さないように文法を変更することも検討しています。

上のプログラムでは,GCD関数を130行で利用しています。これを関数の呼び出しといいます。また,関数を呼び出すとき書く引数(上のプログラムではxとy)を実引数呼びます。GCD関数が呼び出されると,変数a,bに実引数の値が代入されて,関数定義部の各行が順に実行されます。関数の値は,270行にあるような関数名に代入する形のLET文で決定されます。
プログラム単位は,変数名に関して独立した存在です。異なるプログラム単位に同じ名前の変数があってもコンピュータの内部では別の変数です。たとえば,例47の主プログラムの変数x,y,zをa,b,cに書き換えたとしても,プログラムは正常に動作します。

4.1.3  3数の最大公約数

外部関数定義を利用すれば3数の最大公約数を求めるのは簡単です。次のようにすればいいのです。

100 DECLARE EXTERNAL FUNCTION GCD
110 INPUT a,b,c
120 PRINT GCD(GCD(a,b),c)
190 END
200 EXTERNAL FUNCTION GCD(a,b)

例47と同じ 280 END FUNCTION

4.1.4 関数の再帰呼び出し

n!に関して漸化式 0!=1,n!=n・(n-1)! が成立します。ですから,7!は6!に7をかければ求められます。そして,6!は5!に6をかければ求められます。このように数値を逆にたどっていくと,最後は0!=1 という式に行き着いて問題が解決します。
このように,同種の,しかし,より規模の小さい問題に帰着させることを繰り返し実行して問題を解決する手法を再帰と呼びます。
BASICでは,関数を定義するときに自分自身を利用することができます。次のプログラムでは,階乗(factorial)を計算する関数FACTの定義のなかで自分自身を利用しています。コンピュータのプログラミングでは,関数の定義のなかで自分自身を呼び出すことを再帰呼び出しといいます。

例48 階乗の計算

10 DECLARE EXTERNAL FUNCTION FACT
20 INPUT n
30 PRINT FACT(n)
40 END
100 EXTERNAL FUNCTION FACT(n)
110 IF n=0 THEN
120    LET FACT=1
130 ELSE
140    LET FACT=n*FACT(n-1)
150 END IF
160 END FUNCTION 

BASICで関数の再帰呼び出しがうまく機能するのは,関数が呼び出されるごとに変数が新たに割り当てられるからです。たとえば,140行は
LET FACT=FACT(n-1)*n
としても正しく動作しますが,FACT(n-1)の計算の途中でnの値が変化してしまうと正しく動作しないはずです。つまり,関数FACTが呼び出されるごとに,別の場所にその回の呼び出しにだけ通用する変数nが新たに割り当てられているのです。

4.1.5 ユークリッドの互除法

最大公約数を求めるユークリッドの互除法も再帰的な算法の典型例です。BASICではこの算法を次のように書くことができます。

例 49

10 DECLARE EXTERNAL FUNCTION GCD
20 INPUT a,b
30 PRINT GCD(a,b)
40 END
100 EXTERNAL FUNCTION GCD(a,b)
110 LET r=MOD(a,b)
120 IF r=0 THEN LET GCD=b ELSE LET GCD=GCD(b,r)
130 END FUNCTION 

4.2 外部絵定義と外部副プログラム

4.2.1 外部絵定義(PICTURE)

Full BASICには円を描く命令は用意されていませんが,円を描く命令を定義して利用する手段が用意されています。それを絵定義と呼びます。

例50

10 DECLARE EXTERNAL PICTURE circle
20 OPTION ANGLE DEGREES
30 SET WINDOW -8,8,-8,8
40 DRAW circle
50 DRAW circle WITH SCALE(2)
60 DRAW circle WITH SCALE(3,2)
70 DRAW circle WITH SCALE(3,2)*SHIFT(3,4)
80 DRAW circle WITH SCALE(5,3)*ROTATE(60)
90 END
100 EXTERNAL PICTURE circle
110 OPTION ANGLE DEGREES
120 FOR t=0 TO 360
130    PLOT LINES:COS(t),SIN(t);
140 NEXT t
150 END PICTURE 

100行から150行までの部分を外部絵定義といいます。外部絵定義の最初の行にはEXTERNAL PICTUREに続けて絵名を書きます。上のプログラムでは,circleが絵名です。もし必要があれば外部関数定義の場合と同様に引数部を書くこともできます。
絵circleは原点を中心とする半径1の円を描きます。正確にいえば正360角形を描いているだけですが,コンピュータのディスプレーでは円に見えると思います。
プログラム単位はOPTION文に関しても独立です。OPTION ANGLE DEGREESを主プログラムに書いても外部絵定義では角の大きさの単位は変更されません。
絵を実行するのにDRAW文を用います。DRAW文では絵を描くときに原点を中心とする拡大・縮小や回転,あるいは平行移動などの変形を指定することができます。
SCALE(a,b)は原点を中心とするx軸方向にa倍,y軸方向にb倍の拡大です。a,bは負の数でも差し支えありません。a=bの場合には,SCALE(a)のように書くこともできます。
SHIFT(a,b)は,x軸方向にa,y軸方向にbの平行移動です。
ROTATE(α)は,原点を中心とする角αの回転です。
変形は*で結合することで合成することもできます。合成した変形は左から順に実行されます。たとえば,SCALE(3,2)*SHIFT(3,4)は拡大してから平行移動します。

4.2.2 外部副プログラム

外部副プログラムの機能はほとんど外部絵定義のなかに含まれています。文法的にも用いる用語が異なるだけでほとんど同じです。ただし,副プログラムでは図形を変形して描くことはできません。

例51

  100  DECLARE EXTERNAL SUB circle
  120  SET WINDOW -4,4,-4,4
  130  CALL circle(1,-1,2)
  140  END
  200  EXTERNAL SUB circle(a,b,r)
  210  OPTION ANGLE DEGREES
  220  FOR t=0 TO 360
  230      PLOT LINES: a+r*COS(t),b+r*SIN(t);
  240  NEXT t
  250  END SUB 

例51では,点(a,b)を中心とする半径rの円を描く副プログラムcircle(a,b,r)を定義しています。副プログラムを呼び出すのにCALL文を用います。副プログラムに引数があるときは関数と同様の形式で引数を記述します。

4.2.3 変数引数

絵および副プログラムには関数定義と異なる特性があります。それがこれから説明する変数引数です。

例52

10 DECLARE EXTERNAL SUB double
20 LET a=3
30 CALL double(a)
40 PRINT a
50 END
100 EXTERNAL SUB double(x)
110 LET x=x*2
120 END SUB 

例52で副プログラムdoubleは実引数として書いた変数の値を2倍にして返します。副プログラムでは,実引数に変数を書くと,副プログラムの実行中,実引数の変数に割り当てられた領域を引数の領域として使います。したがって,副プログラムの実行中に引数の値を変更すると,実引数の値が変化します。
なお,実引数が変数そのものではないとき,たとえば,30行を
30 CALL double(2*a)
とした場合には,副プログラムに2*aの計算結果の数値が引き渡されるだけです。

4.2.4 不定方程式の整数解

a,b,cが整数の定数であるとき,方程式 a x + b y = c の整数解を探すという問題を考えてみます。なお,この型の方程式は1つ解を持てば無数の解を持つことになるので,ここでは解をひとつ求めればよいものとします。
b=0のときの解は明らかです。cがaで割り切れればx=c/a が解です。yの値は何でもよいのですが,たとえば,y=0とでもしておけばよいでしょう。反対にcがaで割り切れなければ解はありません。
b≠0のときは,aをbで割ったときの商をq,余りをrとします。a=bq+rですから,はじめの方程式をb(qx+y)+rx=cと書き換えることができます。したがって,方程式bu+rv=cの解が求まれば,x=v,y=u-qvが求める解ですし,bu+rv=cに解がなければ解はありません。
ここで,bu+rv=cの解は必ず確定します。なぜかというと,ax+by=cとbx+ry=cを比較したとき,yの係数は確実に0に近づいているからです。yの係数は整数ですから,何回かの繰り返しの後,必ず0になります。

例53

10 DECLARE EXTERNAL SUB solve
20 INPUT a,b,c
30 WHEN EXCEPTION IN
40    CALL solve(a,x,b,y,c)
50    PRINT x,y
60 USE
70    PRINT "解なし"
80 END WHEN
90 END
100 EXTERNAL SUB solve(a,x,b,y,c)
110 IF b=0 THEN
120    IF MOD(c,a)=0 THEN
130       LET x=c/a
140       LET y=0
150    ELSE
160       CAUSE EXCEPTION 999
170    END IF
180 ELSE
190    LET q=INT(a/b)
200    LET r=MOD(a,b)
210    CALL solve(b,u,r,v,c)
220    LET x=v
230    LET y=u-q*v
240 END IF
250 END SUB 

160行のCAUSE EXCEPTION文は指定した例外番号(EXTYPE)の実行時エラー(例外)を起こします。1〜999の例外番号は利用者用として予約されています。
例外を起こした文がWHEN EXECPTION構文で囲まれていなければ,例外は呼び出し元に伝達されます。




次に進む

戻る