【第13章】ゲームを作ろう
2021.10.31
今まで堅苦しいプログラムばかり作ってきましたが、ちょっと楽しいプログラムを作ってみましょう。そう、ゲームのプログラムです。コンピュータゲームというと「ゲームばかりやってないで勉強しなさい」とお父さんお母さんに叱られたとかオンラインゲームで課金し過ぎてお金がなくなったとかあまりいい話を聞きませんがそれはゲームで遊ぶ側の話です。そのゲームのプログラムを作るということになると話は違ってきます。ゲームのプログラム1つ作る過程でプログラミングの高度なテクニックや様々なアルゴリズムを学ぶことができます。筆者はゲームを作りながらコンピュータの勉強をすることは否定しません。ということでこのカテゴリーでも実際にゲームのプログラムを作ってみたいと思います。とはいっても 残念ながらTiny BASIC for Windowsでテキスト画面のみでは、画面いっぱいに敵戦闘機が飛び回るとか、クエストクリアしたら美少女が微笑むようなゲームは無理ですが、ストリングと計算式のみでも知育ゲームやアドベンチャーゲームのようなものはできます(なんかこの筆者、美少女のたとえが多くね?・・・汗)。
[乱数]
ゲームで必須なものは乱数です。乱数とは、規則性のない数の並びのことです。たとえば2、3、4、6なら12の約数を小さい順に並べたものとわかりますが、4、7。2、9だとまったく規則性がありません。ゲームでは敵がどこから襲ってくるかわからない、どこに何が隠れているかわからないなど、ランダムに何かが出てくるという場があります。これを実現かるのが乱数であり、Tiny BASICにはこの乱数を発生させる機能があります。それが次のRNDです。次のプログラムを実行してみてください。
FOR I=0 TO 10
R=RND
PRINT R
NEXT I
実行結果はこのようになります。
図13_1.乱数を発生する
RNDは0以上1未満の乱数を発生する関数(後述)です。ただしひとつ注意することがあります。上のプログラムを何回か実行してみてください。おわかりと思いますが乱数とはいってもコンピュータで発生させる場合ある計算式にしたがって発生させるので、同じプログラムを繰り返し実行すると、発生する数の順番はいつも同じになってしまいます。これではたとえばシューティングゲームに使うにしても、敵が出てくる場所と順番がいつも同じになってしまい、面白みが半減ですね。これを回避するには、次のようにRANDOMIZEという一文をRNDの前に入れます。これはコンピュータの内部時計を用いて、乱数発生の計算式の初期化を行なうものです。
RANDOMIZE
FOR I=0 TO 10
R=RND
PRINT R
NEXT I
これで数回実行してみましょう。今度は発生する数の順番はバラバラになったはずです。では、これで1から10までの整数をランダムに発生させる方法を考えてみましょう。RNDで発生する数は0以上1未満ですから
RANDOMIZE
R=RND*10+1
で 1 から 10 までの数がランダムで得られます。さらに
RANDOMIZE
R=INT(RND*10)
とすると、発生した数の小数点以下が切り捨てられ1から10までの整数がランダムで得られます。INTはInteger(整数の意味)の省略形でINT(数値)で( )内の数値の小数点以下を切り捨てて整数化する関数です。次のプログラムで試してみてください。結果は図13_2のようになります。
RANDOMIZE
FOR I=1 TO 10
R=INT(RND*10+1)
PRINT R
NEXT I
END
図13_2.1から10の整数をランダムに発生させる
[数当てゲームのプログラム]
それでは、これまでの知識を使って簡単なゲームを作ってみましょう。コンピュータが隠した 1 から 100 までの整数を当てるゲームです。次のプログラムを見てください。
1:CLS
2:RANDOMIZE
3:PRINT "***** 数当てゲーム *****"
4:PRINT "今からコンピュータが隠した数を当ててください"
5:PRINT "数は 1 から 100 の整数のみです"
6:REM 数を発生させる
7:R=INT(RND*100)+1
8:REM 数を当てる
9:DATAOK=0
10:WHILE DATAOK=0
11: NUMOK=0
12: WHILE NUMOK=0
13: INPUT " 1~100で数を当ててください",NUM
14: IF NUM=>1 AND NUM<=100 THEN
15: NUMOK=1
16: END IF
17: WEND
18:REM 正解判定
19: IF NUM=R THEN
20: PRINT "正解です。おめでとうございます"
22: DATAOK=1
23: ELSE
24: IF NUM 25: PRINT "小さすぎます"
26: ELSE
27: PRINT "大きすぎます"
28: END IF
29: END IF
30:WEND
31:END
[プログラムの説明]
プログラムスタート部分の1行目のCLSはCLear Screenの省略形で実行画面をすべて消去する命令です。2行目は今、説明した乱数の初期化です。3~5行目はプログラムと遊び方の説明です。
7行目がコンピュータが隠す数を発生させるところですが、ここでは1から100までの数を発生させることにします。
9行目と10、30行目のWHILE~WENDにより、以下の処理は正解が出るまで繰り返し実行されるようになっています。11行目と12、17行目のWHILE~WENDにより、プレイヤーが回答した数が1~100以外の数以外のときは回答入力がやり直しになるようになっています。
19、29行目のIF~END IFは正解不正解の判定です。正解ならこのプログラムを終了するように、不正解なら回答した数が大きすぎか小さすぎかを表示するようにしています。
[関数]
今まで何回かこの言葉を使ってきましたが、命令との違いについて説明してきませんでした。ここまでのレベルのプログラムではこの違いがわからなくてもとくに支障はありませんでしたが、コンピュータのコンピュータらしいプログラムを作るには、知っておいた方がいいことなのでここで説明します。
図13_3.数学の関数
図13_3.は中学校でも習う一次関数のグラフです。数学でいう関数とは伴って変わる数という意味です。プログラミング言語における関数はこの意味もありますがどちらかというと「マジシャンの箱」のようなものと考えてください。
図13_4.プログラミング言語の関数
マジシャンの箱はステッキで触れると中から鳩や万国旗が出てきますが(ネタ古っ)もちろんタネも仕掛けもあるわけです。プログラミング言語の関数とは、何か材料を入れると加工されて完成品が出てくる箱だと思ってください。この箱に入れる材料のことを引数(ひきすう)、箱から出てくる完成品を戻り値(もどりち)といいます。。Tiny Basic for Windowsの関数では引数が不要なものもあります。今まで出てきたサンプルプログラム中にもすでに登場している関数もあります。
[Tiny Basic for Windowsの便利な関数](一部)
SQR(正の数) 平方根を求める
INT(数) 数の小数点以下を切り捨てて整数化する
RND 0以上1未満の乱数を得る(*1)
PI 円周率 3.14159265358979324 を得る(*2)
SIN(数) 正弦を得る(*3)
COS(数) 余弦を得る(*3)
TAN(数) 正接を得る(*3)
図13_5.弧度法(ラジアン)
(*1)正式な書き方はRND(数)。普通に乱数を発生させる場合、数は正の数。省略した場合は正の数が指定されたものとみなす。
(*2)関数というよりプログラミング言語が提供する定数(予約変数)。
(*3)数は弧度法(ラジアン、図13_5)で与える。
サンプルプログラム
A=5
B=SQR(A)
C=INT(B)
PRINT B,C
R=10
S=PI*R^2
PRINT S
END
実行結果
2.2360679774997897 2
314.159265358979324
OK
これらは戻り値が数値の関数なので数値関数とか数学関数とよばれます。プログラミング言語の関数には戻り値がストリングのものもあります。
DATE$ コンピュータが管理しているカレンダーの現在の日付を yyyy/mm/dd形式で得る
TIME$ コンピュータが管理している時計の現在時刻を hh:mm:ss形式で得る
サンプルプログラム
NOW$=DATE$+" "+ TIME$
PRINT NOW$
END
実行結果
2021/11/25 23:16:09
OK
これらの関数はプログラミング言語が提供しているものなので「組込関数」とよばれることもあります。
[乱数]
ゲームで必須なものは乱数です。乱数とは、規則性のない数の並びのことです。たとえば2、3、4、6なら12の約数を小さい順に並べたものとわかりますが、4、7。2、9だとまったく規則性がありません。ゲームでは敵がどこから襲ってくるかわからない、どこに何が隠れているかわからないなど、ランダムに何かが出てくるという場があります。これを実現かるのが乱数であり、Tiny BASICにはこの乱数を発生させる機能があります。それが次のRNDです。次のプログラムを実行してみてください。
FOR I=0 TO 10
R=RND
PRINT R
NEXT I
実行結果はこのようになります。
図13_1.乱数を発生する
RNDは0以上1未満の乱数を発生する関数(後述)です。ただしひとつ注意することがあります。上のプログラムを何回か実行してみてください。おわかりと思いますが乱数とはいってもコンピュータで発生させる場合ある計算式にしたがって発生させるので、同じプログラムを繰り返し実行すると、発生する数の順番はいつも同じになってしまいます。これではたとえばシューティングゲームに使うにしても、敵が出てくる場所と順番がいつも同じになってしまい、面白みが半減ですね。これを回避するには、次のようにRANDOMIZEという一文をRNDの前に入れます。これはコンピュータの内部時計を用いて、乱数発生の計算式の初期化を行なうものです。
RANDOMIZE
FOR I=0 TO 10
R=RND
PRINT R
NEXT I
これで数回実行してみましょう。今度は発生する数の順番はバラバラになったはずです。では、これで1から10までの整数をランダムに発生させる方法を考えてみましょう。RNDで発生する数は0以上1未満ですから
RANDOMIZE
R=RND*10+1
で 1 から 10 までの数がランダムで得られます。さらに
RANDOMIZE
R=INT(RND*10)
とすると、発生した数の小数点以下が切り捨てられ1から10までの整数がランダムで得られます。INTはInteger(整数の意味)の省略形でINT(数値)で( )内の数値の小数点以下を切り捨てて整数化する関数です。次のプログラムで試してみてください。結果は図13_2のようになります。
RANDOMIZE
FOR I=1 TO 10
R=INT(RND*10+1)
PRINT R
NEXT I
END
図13_2.1から10の整数をランダムに発生させる
[数当てゲームのプログラム]
それでは、これまでの知識を使って簡単なゲームを作ってみましょう。コンピュータが隠した 1 から 100 までの整数を当てるゲームです。次のプログラムを見てください。
1:CLS
2:RANDOMIZE
3:PRINT "***** 数当てゲーム *****"
4:PRINT "今からコンピュータが隠した数を当ててください"
5:PRINT "数は 1 から 100 の整数のみです"
6:REM 数を発生させる
7:R=INT(RND*100)+1
8:REM 数を当てる
9:DATAOK=0
10:WHILE DATAOK=0
11: NUMOK=0
12: WHILE NUMOK=0
13: INPUT " 1~100で数を当ててください",NUM
14: IF NUM=>1 AND NUM<=100 THEN
15: NUMOK=1
16: END IF
17: WEND
18:REM 正解判定
19: IF NUM=R THEN
20: PRINT "正解です。おめでとうございます"
22: DATAOK=1
23: ELSE
24: IF NUM
26: ELSE
27: PRINT "大きすぎます"
28: END IF
29: END IF
30:WEND
31:END
[プログラムの説明]
プログラムスタート部分の1行目のCLSはCLear Screenの省略形で実行画面をすべて消去する命令です。2行目は今、説明した乱数の初期化です。3~5行目はプログラムと遊び方の説明です。
7行目がコンピュータが隠す数を発生させるところですが、ここでは1から100までの数を発生させることにします。
9行目と10、30行目のWHILE~WENDにより、以下の処理は正解が出るまで繰り返し実行されるようになっています。11行目と12、17行目のWHILE~WENDにより、プレイヤーが回答した数が1~100以外の数以外のときは回答入力がやり直しになるようになっています。
19、29行目のIF~END IFは正解不正解の判定です。正解ならこのプログラムを終了するように、不正解なら回答した数が大きすぎか小さすぎかを表示するようにしています。
[関数]
今まで何回かこの言葉を使ってきましたが、命令との違いについて説明してきませんでした。ここまでのレベルのプログラムではこの違いがわからなくてもとくに支障はありませんでしたが、コンピュータのコンピュータらしいプログラムを作るには、知っておいた方がいいことなのでここで説明します。
図13_3.数学の関数
図13_3.は中学校でも習う一次関数のグラフです。数学でいう関数とは伴って変わる数という意味です。プログラミング言語における関数はこの意味もありますがどちらかというと「マジシャンの箱」のようなものと考えてください。
図13_4.プログラミング言語の関数
マジシャンの箱はステッキで触れると中から鳩や万国旗が出てきますが(ネタ古っ)もちろんタネも仕掛けもあるわけです。プログラミング言語の関数とは、何か材料を入れると加工されて完成品が出てくる箱だと思ってください。この箱に入れる材料のことを引数(ひきすう)、箱から出てくる完成品を戻り値(もどりち)といいます。。Tiny Basic for Windowsの関数では引数が不要なものもあります。今まで出てきたサンプルプログラム中にもすでに登場している関数もあります。
[Tiny Basic for Windowsの便利な関数](一部)
SQR(正の数) 平方根を求める
INT(数) 数の小数点以下を切り捨てて整数化する
RND 0以上1未満の乱数を得る(*1)
PI 円周率 3.14159265358979324 を得る(*2)
SIN(数) 正弦を得る(*3)
COS(数) 余弦を得る(*3)
TAN(数) 正接を得る(*3)
図13_5.弧度法(ラジアン)
(*1)正式な書き方はRND(数)。普通に乱数を発生させる場合、数は正の数。省略した場合は正の数が指定されたものとみなす。
(*2)関数というよりプログラミング言語が提供する定数(予約変数)。
(*3)数は弧度法(ラジアン、図13_5)で与える。
サンプルプログラム
A=5
B=SQR(A)
C=INT(B)
PRINT B,C
R=10
S=PI*R^2
PRINT S
END
実行結果
2.2360679774997897 2
314.159265358979324
OK
これらは戻り値が数値の関数なので数値関数とか数学関数とよばれます。プログラミング言語の関数には戻り値がストリングのものもあります。
DATE$ コンピュータが管理しているカレンダーの現在の日付を yyyy/mm/dd形式で得る
TIME$ コンピュータが管理している時計の現在時刻を hh:mm:ss形式で得る
サンプルプログラム
NOW$=DATE$+" "+ TIME$
PRINT NOW$
END
実行結果
2021/11/25 23:16:09
OK
これらの関数はプログラミング言語が提供しているものなので「組込関数」とよばれることもあります。
【第12章】データをブログラム中に置く
2021.03.10
前章のプログラムは出席番号、氏名、テストの得点すべてをキーボードから入力するものでした。作ったプログラムを実行してみていかがでしたか。データ入力がかなりたいへんだと感じた方も多いのではないでしょうか。小学校の先生なら(原則的には)すべての教科を担任の先生が教えます。中学・高校の先生なら担当教科は一つでも同じ学年の複数のクラスを担当することも多いでしょう。その度に出席番号、氏名、得点を入力することになり、入力が多くなればなるほど誤入力(誤操作)が発生する確率も高くなります。これをなんとかしましょう。
次のプログラムを見てください。
1: *SAKKYOKUKA
2: DATA "J.S.バッハ",1665,1750
3: DATA "ベートーヴェン",1770,1826
4: DATA "ショパン",1810,1849
5: DATA "チャイコフスキー",1840,1893
6: DATA "ドビュッシー",1862,1918
7: DATA "//",0,0
8: PRINT "偉大なる作曲家"
9: RESTORE *SAKKYOKUKA
10: NAMAE$=""
11: WHILE NAMAE$<>"//"
12: READ NAMAE$,SEINEN,BOTSUNEN
13: IF NAMAE$<>"//" THEN
14: PRINT NAMAE$;" ";SEINEN;" ~";BOTSUNEN;
15: END IF
16: WEND
17: END
[プログラム中にデータを入れる]
Tiny BASICでは固定的なデータはDATA文でプログラム中に置いてそれをREAD命令で変数に読み込むことができます。次のプログラムを見てください。1行目はプログラム中の特定の場所に目印を付けるものでラベルといいます。ラベルを付けるときは半角の*に続けてラベル名を指定します。ラベル名の付け方の規則は変数名と同じです。
2~4行目はDATAという指示語から始まって、その後に何か書いてあります。これはプログラム中に入れたデータです。このサンプルはクラシック音楽の作曲家の名前と生年、没年です。ラベルやDATA文は何かの動作をするわけではないので、プログラムを実行すると8行目から実行されます。
順序が入れ替わりますが12行目のREADはDATA文でプログラム中に埋め込まれたデータを変数に読み込む命令です。9行目のRESTOREはREAD命令で読み込むデータがどこからかを指示するものです。ここで1行目に付けたラベルの意味が出てきます。
当然のことですが、DATA文で入れるデータとREAD文で読み込む変数の型は一致していなくてはなりません。DATA文で用意されたデータの個数(a)とREAD文で指定した変数の個数(b)が一致していないとき、a<bだと余ったデータは無視されます。反対にa>bだと「お~い、データがないぞ」というエラーになります。このプログラムを実行すると図12_1.のようになります。
図12_1.作曲家の生年と没年
[プログラムはわかりやすく書こう]
DATA文で入れるデータはデータの型、読み込む変数の数が合っていればプログラム中のどこへ置いてもかまいません。たとえば次のプログラムと上のプログラムは同じ動作をします。
1: *SAKKYOKUKA
2: DATA "J.S.バッハ",1665,1750, "ベートーヴェン",1770,1826
3: DATA "ショパン",1810,1849,"チャイコフスキー",1840
4: DATA 1893,"ドビュッシー",1862,1918, "//",0,0
5: PRINT "偉大なる作曲家"
6: RESTORE *SAKKYOKUKA
7: NAMAE$=""
8: WHILE NAMAE$<>"//"
9: READ NAMAE$,SEINEN,BOTSUNEN
10: IF NAMAE$<>"//" THEN
11: PRINT NAMAE$;" ";SEINEN;" ~";BOTSUNEN
12: END IF
13: WEND
14: END
Tiny BASICではDATA文は制御文ブロックの中に置いてもかまいません(DATA文は繰り返し処理や選択処理の対象にはなりません)。次のプログラムも上2つと同じ動作をします。
1: RESTORE *SAKKYOKUKA
2: *SAKKYOKUKA
3: PRINT "偉大なる作曲家"
4: DATA "j.S.バッハ",1665,1750
5: NAMAE$=""
6: DATA "ベートーヴェン",1770,1826
7: WHILE NAMAE$<>"//"
8: READ NAMAE$,SEINEN,BOTSUNEN
9: DATA "ショパン",1810,1849
10: IF NAMAE$<>"//" THEN
11: DATA "チャイコフスキー",1840,1893
12: LIFE= BOTSUNEN-SEINEN
13: PRINT NAMAE$;" ";SEINEN;" ~";BOTSUNEN
14: END IF
15: DATA "ドビュッシー",1862,1918
16: WEND
17: DATA "//",0,0
18: END
しかし、プログラム上は正しく動作するといっても、上2つのようなプログラムは見辛いというかわかりにくいことこの上ないですね。後でプログラムの修正の必要が出てきたとき作った当人でもかなり苦労すると思います。プログラムは後々のことを考えてわかりやすく書きましょう。次のプログラムを見てください。
1: REM 読み込むデータ
2: *SAKKYOKUKA
3: DATA "J.S.バッハ",1665,1750
4: DATA "ベートーヴェン",1770,1826
5: DATA "ショパン",1810,1849
6: DATA "チャイコフスキー",1840,1893
7: DATA "ドビュッシー",1862,1918
8: DATA "//",0,0
9:
10: REM オープニング
11: PRINT "偉大なる作曲家"
12:
13: REM データの読み込みと表示
14: RESTORE *SAKKYOKUKA
15: NAMAE$=""
16: WHILE NAMAE$<>"//"
17: READ NAMAE$,SEINEN,BOTSUNEN '名前、生年、没年
18: IF NAMAE$<>"//" THEN
19: PRINT NAMAE$;" ";SEINEN;" ~";BOTSUNEN
20: END IF
21: WEND
22:
23: REM 修了
24: END
わかりやすいプログラムを書く第一歩は、一連の処理はひとかたまりにしておくことです。上の例では1~8行目は読み込むデータ、10~11行目は誤実行防止のためのプログラム名表示、13~21行目はデータの読み込みと表示の繰り返し、23~24行目はプログラム終了、となっています。そしてこれらの間に9、22行目のような空行が入っています。Tiny BASICではプログラム中に何行空行を入れても差支えありません(実行時は無視されます)。
これだけでもだいぶプログラムは見やすくなりますが、もう一歩進みましょう。1、10行目は文頭にREMと書かれています。REMはremarks(備考)の意味で、これがあるとその行のREM以後は注記とみなされます(実行時は無視されます)。REMの代わりに ' (半角のシングルクォーテーション)にすることもできます。このようにプログラムの要所要所に注記を入れておくと「ここで何をやっているか」が一見してわかるようになります。
【この章のまとめ】
DATA文でプログラム中にデータを置くことができる。
DATA文で置いたプログラムはREAD命令で変数に読み込むことができる。
RESTORE文でREAD文で読み込むDATA文がどこからかを指示できる。
次のプログラムを見てください。
1: *SAKKYOKUKA
2: DATA "J.S.バッハ",1665,1750
3: DATA "ベートーヴェン",1770,1826
4: DATA "ショパン",1810,1849
5: DATA "チャイコフスキー",1840,1893
6: DATA "ドビュッシー",1862,1918
7: DATA "//",0,0
8: PRINT "偉大なる作曲家"
9: RESTORE *SAKKYOKUKA
10: NAMAE$=""
11: WHILE NAMAE$<>"//"
12: READ NAMAE$,SEINEN,BOTSUNEN
13: IF NAMAE$<>"//" THEN
14: PRINT NAMAE$;" ";SEINEN;" ~";BOTSUNEN;
15: END IF
16: WEND
17: END
[プログラム中にデータを入れる]
Tiny BASICでは固定的なデータはDATA文でプログラム中に置いてそれをREAD命令で変数に読み込むことができます。次のプログラムを見てください。1行目はプログラム中の特定の場所に目印を付けるものでラベルといいます。ラベルを付けるときは半角の*に続けてラベル名を指定します。ラベル名の付け方の規則は変数名と同じです。
2~4行目はDATAという指示語から始まって、その後に何か書いてあります。これはプログラム中に入れたデータです。このサンプルはクラシック音楽の作曲家の名前と生年、没年です。ラベルやDATA文は何かの動作をするわけではないので、プログラムを実行すると8行目から実行されます。
順序が入れ替わりますが12行目のREADはDATA文でプログラム中に埋め込まれたデータを変数に読み込む命令です。9行目のRESTOREはREAD命令で読み込むデータがどこからかを指示するものです。ここで1行目に付けたラベルの意味が出てきます。
当然のことですが、DATA文で入れるデータとREAD文で読み込む変数の型は一致していなくてはなりません。DATA文で用意されたデータの個数(a)とREAD文で指定した変数の個数(b)が一致していないとき、a<bだと余ったデータは無視されます。反対にa>bだと「お~い、データがないぞ」というエラーになります。このプログラムを実行すると図12_1.のようになります。
図12_1.作曲家の生年と没年
[プログラムはわかりやすく書こう]
DATA文で入れるデータはデータの型、読み込む変数の数が合っていればプログラム中のどこへ置いてもかまいません。たとえば次のプログラムと上のプログラムは同じ動作をします。
1: *SAKKYOKUKA
2: DATA "J.S.バッハ",1665,1750, "ベートーヴェン",1770,1826
3: DATA "ショパン",1810,1849,"チャイコフスキー",1840
4: DATA 1893,"ドビュッシー",1862,1918, "//",0,0
5: PRINT "偉大なる作曲家"
6: RESTORE *SAKKYOKUKA
7: NAMAE$=""
8: WHILE NAMAE$<>"//"
9: READ NAMAE$,SEINEN,BOTSUNEN
10: IF NAMAE$<>"//" THEN
11: PRINT NAMAE$;" ";SEINEN;" ~";BOTSUNEN
12: END IF
13: WEND
14: END
Tiny BASICではDATA文は制御文ブロックの中に置いてもかまいません(DATA文は繰り返し処理や選択処理の対象にはなりません)。次のプログラムも上2つと同じ動作をします。
1: RESTORE *SAKKYOKUKA
2: *SAKKYOKUKA
3: PRINT "偉大なる作曲家"
4: DATA "j.S.バッハ",1665,1750
5: NAMAE$=""
6: DATA "ベートーヴェン",1770,1826
7: WHILE NAMAE$<>"//"
8: READ NAMAE$,SEINEN,BOTSUNEN
9: DATA "ショパン",1810,1849
10: IF NAMAE$<>"//" THEN
11: DATA "チャイコフスキー",1840,1893
12: LIFE= BOTSUNEN-SEINEN
13: PRINT NAMAE$;" ";SEINEN;" ~";BOTSUNEN
14: END IF
15: DATA "ドビュッシー",1862,1918
16: WEND
17: DATA "//",0,0
18: END
しかし、プログラム上は正しく動作するといっても、上2つのようなプログラムは見辛いというかわかりにくいことこの上ないですね。後でプログラムの修正の必要が出てきたとき作った当人でもかなり苦労すると思います。プログラムは後々のことを考えてわかりやすく書きましょう。次のプログラムを見てください。
1: REM 読み込むデータ
2: *SAKKYOKUKA
3: DATA "J.S.バッハ",1665,1750
4: DATA "ベートーヴェン",1770,1826
5: DATA "ショパン",1810,1849
6: DATA "チャイコフスキー",1840,1893
7: DATA "ドビュッシー",1862,1918
8: DATA "//",0,0
9:
10: REM オープニング
11: PRINT "偉大なる作曲家"
12:
13: REM データの読み込みと表示
14: RESTORE *SAKKYOKUKA
15: NAMAE$=""
16: WHILE NAMAE$<>"//"
17: READ NAMAE$,SEINEN,BOTSUNEN '名前、生年、没年
18: IF NAMAE$<>"//" THEN
19: PRINT NAMAE$;" ";SEINEN;" ~";BOTSUNEN
20: END IF
21: WEND
22:
23: REM 修了
24: END
わかりやすいプログラムを書く第一歩は、一連の処理はひとかたまりにしておくことです。上の例では1~8行目は読み込むデータ、10~11行目は誤実行防止のためのプログラム名表示、13~21行目はデータの読み込みと表示の繰り返し、23~24行目はプログラム終了、となっています。そしてこれらの間に9、22行目のような空行が入っています。Tiny BASICではプログラム中に何行空行を入れても差支えありません(実行時は無視されます)。
これだけでもだいぶプログラムは見やすくなりますが、もう一歩進みましょう。1、10行目は文頭にREMと書かれています。REMはremarks(備考)の意味で、これがあるとその行のREM以後は注記とみなされます(実行時は無視されます)。REMの代わりに ' (半角のシングルクォーテーション)にすることもできます。このようにプログラムの要所要所に注記を入れておくと「ここで何をやっているか」が一見してわかるようになります。
【この章のまとめ】
DATA文でプログラム中にデータを置くことができる。
DATA文で置いたプログラムはREAD命令で変数に読み込むことができる。
RESTORE文でREAD文で読み込むDATA文がどこからかを指示できる。
2021.03.10 15:30
| 固定リンク
| Tiny BASICによるプログラミング
【第11章】配列を使う(1)データの保存と再利用
2020.06.01
繰り返し処理、選択処理では、まだまだお話したいことがたくさんあるのですが、このカテゴリーの目的には1週間~10日ほどでプログラミングをひととおりやってみましょうということもあるので、とりあえず今は先に進みます。
さて、ここまでやってきたテストの平均点を求めるプログラムですが、これをもう少し改造してみましょう。平均点が出たところで、補習対象者(仮に平均点の2分の1に達しなかった生徒とします・・・厳し過ぎますか?)を抽出して表示するようにします。どうしたらよいでしょう。
変数はたしかにデータを記憶できるのですが、それは次に同じ変数に別のデータが入ってくるまでの間です。たとえば、次のようなプログラムでは①で3を入力したとするとこの時点で変数Aには3が入っていますが②で5を入力したとすると、この時点で最初に入力した3は消えてしまいます。
① INPUT "数値を入力してください",A
B=2
C=A+B
PRINT C
② INPUT "数値を入力してください",A
①で入力した3を保存しておくにはどうしたらよいでしょう?これにはいくつかの方法がありますが、ここでは配列を使ってデータを保存する方法を学びましょう。
[配列]
図11_1.配列の概念
ここにハイツ立山というアパートがあります。1号室は田中さん、2号室は青木さん、3号室は木下さん、4号室は空室、5号室は渡辺さんです。これをTiny BASICっぽく書くと
NAMAE$=TATEYAMA$(1)で変数NAMAE$には"田中"と入ります。同じようにNAMAE$=TATEYAMA$(2)とすると変数NAMAE$は"青木"となります。またTATEYAMA$(4)="高橋"とすると4号室には高橋さんが入居します。配列とはこのようなものです。配列を使うには使用前にDIMで使用宣言をする必要があります。宣言の方法はこうなります。
DIM 配列名(正の整数)
ここで配列名の規則は変数と同じで、半角英数字と _ (アンダーバー)の組み合わせで30文字以内(先頭の1文字は英字)で英字は大文字小文字を区別しません。配列名の後ろに $ を付けるとストリングも扱えますが同じ配列の中に数値とストリングを混在させることはできません。
配列名の後ろの( )内の正の整数は配列のサイズ(いくつ用意するか)で、添え字と呼ばれます。配列の添え字は0から始まります。また
DIM 配列名1(正の整数),配列名2(正の整数),・・・
のように1つのDIMステートメントで複数の配列を使用宣言することもできます。たとえば
DIM BANGO(5),NAMAE$(5),TENSU(5)
とすると、数値が入るBANGO(0)~BANGO(5)、TENSU(0)~TENSU(5)、ストリングが入るNAMAE$(0)~NAMAE$(5)という配列が使えるようになります。このようにして使用宣言された配列は変数と同様に使うことができます(Tiny BASIC for Windowsでは配列変数と呼んでいます)。たとえば次のプログラムを実行すると図11_2.のようになります。
DIM BANGO(5),NAMAE$(5),TENSU(5)
INPUT "出席番号 ";BANGO(0)
INPUT "氏名 ";NAMAE$(0)
INPUT "点数 ";TENSU(0)
INPUT "出席番号 ";BANGO(1)
INPUT "氏名 ";NAMAE$(1)
INPUT "点数 ";TENSU(1)
図11_2.配列にデータを入力する
ただしこのような使い方では配列を使う意味はほとんどありません。配列の添え字には変数も使えます。これを使って前章でやったプログラムを次のように改造してみましょう。このプログラムはテストの平均点を求めた後、それぞれの生徒の得点が平均とどれくらい差があるかを表示するものです。得点-平均点で、平均点より高ければ正、低ければ負となります。入力したデータは配列に保存しておき、平均点を求めた後に再利用します。
1: DIM BANGO$(100),NAMAE$(100),TENSU(100)
2: PRINT "***テストの平均点***"
3: GOKEI=0
4: NINZU=0
5: TOKUTEN=-1
6: I=0
7: NUM$="-1"
8: WHILE NUM$<>"999"
9: INPUT "番号(999=END) ",NUM$
10: IF NUM$<>"999" THEN
11: INPUT "氏名 ",SHIMEI$
12: INPUT "点数 ",TOKUTEN
13: BANGO$(I)=NUM$
14: NAMAE$(I)=SHIMEI$
15: TENSU(I)=TOKUTEN
16: GOKEI=GOKEI+TOKUTEN
17: NINZU=NINZU+1
18: I=I+1
19: END IF
20: WEND
21: HEIKIN=INT(GOKEI/NINZU)
22: PRINT "平均点は";HEIKIN
23: LASTNUM=I-1
24: FOR I=0 TO LASTNUM
25: TENSA=TENSU(I)-HEIKIN
26: PRINT BANGO$(I);" ";NAMAE$(I),TENSU(I),TENSA
27: NEXT I
28: END
前章のプログラムに追加・変更された部分について説明します。
1行目は配列の宣言です。ここでは番号、氏名、得点を保存しておく配列を0から50で51個ずつ(51人分)用意しています。
6行目で配列の添え字用の変数の初期値を 0 としておきます。
今回は出席番号はストリング型としています。出席番号は普通計算の対象にはならないのと、出席番号に数字以外の文字が含まれる場合にも対応するためです。7行目では出席番号にあり得ないデータとして -1 を代入しておきます。
9行目で出席番号を入力します。ここで"999"が入力されたらデータ入力終了です。
出席番号が"999"でなければまだデータ入力は続きますから、氏名、点数を入れるのは11~12行目(WHILE~WENDのループ内)になります。
13~15行目は入力したデータを配列に保存しておくところです。1回目のループでは添え字用変数 I の値は 0 なので出席番号はBANGO$(0)、氏名はNAMAE$(0)、点数はTENSU(0)に代入(保存)されます。そして18行目で添え字の値を 1 加算して次の生徒のデータは次の配列に入るようにします。
21行目はデータ入力が全部終わって平均を計算するところですが、ここでは INT(GOKEI/NINZU) とINT( )の( )内に点数合計÷人数の計算式が書かれています。このINT( )は関数とよばれるもののひとつです。関数については後で詳しく説明します。INT( )は( )内の数を整数化するものです。( )内は数値である必要があります。( )内の数の小数第一位以下を切り捨てるものなので小数点第一位を四捨五入するのではないことに注意してください。
23行目はデータを保存した配列の最後の添え字を保存しています。このプログラムではデータ入力のループを抜けたところで配列の添え字 I は最後の値+1になっているので I-1の値を別の変数を別の変数に保存しておきます。
そして24~27行目が、配列に保存したデータを再利用して、平均点との差を計算し、結果をディスプレイに表示する部分です。このプログラムを実行すると、図11_3.のようになります。
図11_3.実行画面
【この章のまとめ】
変数名の後ろに(数字)が付いたものを配列(配列変数)という。
配列を使うとデータの保存、再利用ができる。
配列を使用するには DIMステートメントを使う。書き方は
DIM 配列名(正の整数)
配列名の規則は変数名と同じである。正の整数は配列のサイズ(添え字の最大値)である。添え字の最小値は 0 である。
INT(数値)は( )内の数値を整数化(小数点第1位以下を切り捨てる)する。関数のひとつである。
【演習7】次のプログラム仕様書に基づくプログラムを作りなさい。
さて、ここまでやってきたテストの平均点を求めるプログラムですが、これをもう少し改造してみましょう。平均点が出たところで、補習対象者(仮に平均点の2分の1に達しなかった生徒とします・・・厳し過ぎますか?)を抽出して表示するようにします。どうしたらよいでしょう。
変数はたしかにデータを記憶できるのですが、それは次に同じ変数に別のデータが入ってくるまでの間です。たとえば、次のようなプログラムでは①で3を入力したとするとこの時点で変数Aには3が入っていますが②で5を入力したとすると、この時点で最初に入力した3は消えてしまいます。
① INPUT "数値を入力してください",A
B=2
C=A+B
PRINT C
② INPUT "数値を入力してください",A
①で入力した3を保存しておくにはどうしたらよいでしょう?これにはいくつかの方法がありますが、ここでは配列を使ってデータを保存する方法を学びましょう。
[配列]
図11_1.配列の概念
ここにハイツ立山というアパートがあります。1号室は田中さん、2号室は青木さん、3号室は木下さん、4号室は空室、5号室は渡辺さんです。これをTiny BASICっぽく書くと
NAMAE$=TATEYAMA$(1)で変数NAMAE$には"田中"と入ります。同じようにNAMAE$=TATEYAMA$(2)とすると変数NAMAE$は"青木"となります。またTATEYAMA$(4)="高橋"とすると4号室には高橋さんが入居します。配列とはこのようなものです。配列を使うには使用前にDIMで使用宣言をする必要があります。宣言の方法はこうなります。
DIM 配列名(正の整数)
ここで配列名の規則は変数と同じで、半角英数字と _ (アンダーバー)の組み合わせで30文字以内(先頭の1文字は英字)で英字は大文字小文字を区別しません。配列名の後ろに $ を付けるとストリングも扱えますが同じ配列の中に数値とストリングを混在させることはできません。
配列名の後ろの( )内の正の整数は配列のサイズ(いくつ用意するか)で、添え字と呼ばれます。配列の添え字は0から始まります。また
DIM 配列名1(正の整数),配列名2(正の整数),・・・
のように1つのDIMステートメントで複数の配列を使用宣言することもできます。たとえば
DIM BANGO(5),NAMAE$(5),TENSU(5)
とすると、数値が入るBANGO(0)~BANGO(5)、TENSU(0)~TENSU(5)、ストリングが入るNAMAE$(0)~NAMAE$(5)という配列が使えるようになります。このようにして使用宣言された配列は変数と同様に使うことができます(Tiny BASIC for Windowsでは配列変数と呼んでいます)。たとえば次のプログラムを実行すると図11_2.のようになります。
DIM BANGO(5),NAMAE$(5),TENSU(5)
INPUT "出席番号 ";BANGO(0)
INPUT "氏名 ";NAMAE$(0)
INPUT "点数 ";TENSU(0)
INPUT "出席番号 ";BANGO(1)
INPUT "氏名 ";NAMAE$(1)
INPUT "点数 ";TENSU(1)
図11_2.配列にデータを入力する
ただしこのような使い方では配列を使う意味はほとんどありません。配列の添え字には変数も使えます。これを使って前章でやったプログラムを次のように改造してみましょう。このプログラムはテストの平均点を求めた後、それぞれの生徒の得点が平均とどれくらい差があるかを表示するものです。得点-平均点で、平均点より高ければ正、低ければ負となります。入力したデータは配列に保存しておき、平均点を求めた後に再利用します。
1: DIM BANGO$(100),NAMAE$(100),TENSU(100)
2: PRINT "***テストの平均点***"
3: GOKEI=0
4: NINZU=0
5: TOKUTEN=-1
6: I=0
7: NUM$="-1"
8: WHILE NUM$<>"999"
9: INPUT "番号(999=END) ",NUM$
10: IF NUM$<>"999" THEN
11: INPUT "氏名 ",SHIMEI$
12: INPUT "点数 ",TOKUTEN
13: BANGO$(I)=NUM$
14: NAMAE$(I)=SHIMEI$
15: TENSU(I)=TOKUTEN
16: GOKEI=GOKEI+TOKUTEN
17: NINZU=NINZU+1
18: I=I+1
19: END IF
20: WEND
21: HEIKIN=INT(GOKEI/NINZU)
22: PRINT "平均点は";HEIKIN
23: LASTNUM=I-1
24: FOR I=0 TO LASTNUM
25: TENSA=TENSU(I)-HEIKIN
26: PRINT BANGO$(I);" ";NAMAE$(I),TENSU(I),TENSA
27: NEXT I
28: END
前章のプログラムに追加・変更された部分について説明します。
1行目は配列の宣言です。ここでは番号、氏名、得点を保存しておく配列を0から50で51個ずつ(51人分)用意しています。
6行目で配列の添え字用の変数の初期値を 0 としておきます。
今回は出席番号はストリング型としています。出席番号は普通計算の対象にはならないのと、出席番号に数字以外の文字が含まれる場合にも対応するためです。7行目では出席番号にあり得ないデータとして -1 を代入しておきます。
9行目で出席番号を入力します。ここで"999"が入力されたらデータ入力終了です。
出席番号が"999"でなければまだデータ入力は続きますから、氏名、点数を入れるのは11~12行目(WHILE~WENDのループ内)になります。
13~15行目は入力したデータを配列に保存しておくところです。1回目のループでは添え字用変数 I の値は 0 なので出席番号はBANGO$(0)、氏名はNAMAE$(0)、点数はTENSU(0)に代入(保存)されます。そして18行目で添え字の値を 1 加算して次の生徒のデータは次の配列に入るようにします。
21行目はデータ入力が全部終わって平均を計算するところですが、ここでは INT(GOKEI/NINZU) とINT( )の( )内に点数合計÷人数の計算式が書かれています。このINT( )は関数とよばれるもののひとつです。関数については後で詳しく説明します。INT( )は( )内の数を整数化するものです。( )内は数値である必要があります。( )内の数の小数第一位以下を切り捨てるものなので小数点第一位を四捨五入するのではないことに注意してください。
23行目はデータを保存した配列の最後の添え字を保存しています。このプログラムではデータ入力のループを抜けたところで配列の添え字 I は最後の値+1になっているので I-1の値を別の変数を別の変数に保存しておきます。
そして24~27行目が、配列に保存したデータを再利用して、平均点との差を計算し、結果をディスプレイに表示する部分です。このプログラムを実行すると、図11_3.のようになります。
図11_3.実行画面
【この章のまとめ】
変数名の後ろに(数字)が付いたものを配列(配列変数)という。
配列を使うとデータの保存、再利用ができる。
配列を使用するには DIMステートメントを使う。書き方は
DIM 配列名(正の整数)
配列名の規則は変数名と同じである。正の整数は配列のサイズ(添え字の最大値)である。添え字の最小値は 0 である。
INT(数値)は( )内の数値を整数化(小数点第1位以下を切り捨てる)する。関数のひとつである。
【演習7】次のプログラム仕様書に基づくプログラムを作りなさい。
【第10章】平均を求めるプログラム(2)選択処理
2020.03.30
不定回の繰り返し処理のやり方はおわかりいただけたでしょうか。この章ではさらに「条件によって処理内容を変える」という方法を学びます。コンピュータの最もコンピュータらしい部分で、これができるゆえに、筆者が小学生だった昭和40年頃は、コンピュータは「人工頭脳」と呼ばれたりしたものでした。今でも中国語では「電脳」といい、日本でも電脳少女など、ホビーの世界ではこの言い方が使われてますね。
話を本題に戻します。回数が決まってないとはいえ、データの件数が無限ということはそうそうあることではありません。テストの平均点を求めるプログラムなら受験者の点数すべて入力し終わったらループは終了で、平均の計算に移ります。では、何人受験したかわからない学力テストの平均点を求めるプログラムで「ここでデータ入力終わり」ということをどうやってプログラムに知らせたらよいでしょう。これにはいくつかの方法がありますが、よく使われる方法は、「あり得ないデータ」が入ってきたところでループから抜けるというものです。たとえば
1)テストの点数は0点~100点の範囲である
2)点数に999が入力されたらデータ入力のループを抜ける
3)児童の出席番号(または受験番号)は1、2、3、・・・、Nとなっていて欠番はないものとする。
という前提で、不定回数の繰り返し処理によるテストの平均を求めるアルゴリズムを考えてみます。基本的な流れは次のようになります。
図10_1.受験者人数が一定でない学力テストの平均点を求める
上の図で、入力された点数が(たとえば)75のときと999のときでは処理の流れがどう変わるかを辿ってみてください。このアルゴリズムをTiny BASICでコーディング(プログラミング言語を使って実際にプログラムを作成すること)すると次のようになります。
1: PRINT "***テストの平均点***"
2: GOKEI=0
3: NINZU=0
4: BANGO=0
5: TENSU=-1
6: WHILE TENSU<>999
7: BANGO=BANGO+1
8: PRINT BANGO;"番";
9: INPUT "点数(999=END)",TENSU
10: IF TENSU<>999 THEN
11: GOKEI=GOKEI+TENSU
12: NINZU=NINZU+1
13: END IF
14: WEND
15: HEIKIN=GOKEI/NINZU
16: PRINT "平均点は";HEIKIN
17: END
1~3行目はもう説明不要ですね。
4行目の変数BANGOは出席番号です。え?これ、NINZUを使えばいいんじゃない?と思われる方もいると思いますが、それはあまり感心しません。今ここでは番号の欠番はないものとしていますが、たとえば学校の場合、転校などによっていなくなった児童がいたりします。筆者の知る限りではこういうときその児童の出席番号は欠番となります(学年が変わってクラス替えがない限り1つずつ出席番号が前へずれるということはありません)。塾・予備校などの学力テストなら受験申し込みはしたものの受験はしなかったという児童・生徒がいるかもしれません。そういうとき、データ件数の値と出席番号(受験番号)の値が一致しなくなることは容易に想像がつきます。そのようなことにも対処できるようなプログラムにするにはデータ件数と出席番号は別々の変数にした方が後でプログラムの修正が楽になります。
実際プロのプログラマが作ったプログラムを拝見すると後から機能の追加の要求がきたり、処理内容の一部に変更があってプログラムを修正(メンテナンス)する必要が出てきたときのことを想定して書かれていることがうかがえます。
5行目はループに入るために、その条件となる変数TENSUに「得点の範囲外でループ終了条件とも違う値」を代入しています。WHILEの後ろの条件はループ実行条件であることを思い出してください。
7~9行目、これで点数入力のループに入ったのですから出席番号を +1 して、点数入力です。
10行目がこの章の主役、IF文です。ここでは「変数TENSUの値が999でないときはTHEN以下の処理をせよ」ということになります。どこまでかを示すのが13行目にあるEND IFです(詳細は次節で)。
繰り返し処理はここまでなので14行目にはWENDがあり、6行目のループ実行判定に戻ります。もし今、入力した内容がループ終了条件の999だったら、11~12行目の処理は行なわずに6行目に戻ってくることになります。ここでTENSUの値は999ですから、ループ実行条件は成立せず(ループ終了条件が成立し)、15行目以下に飛びます。平均を計算し、表示してプログラムは終わります。
[IF ~ END IF制御文]
このプログラム中に IF~END IFというのが出てきます。勘の良い方なら「ん?この構文は?FOR ~ NEXTやWHILE ~ WENDのように条件によってプログラムの実行順序を変える制御文かな?」と思ったことでしょう。その通りです。ただ、IF ~ END IFは繰り返し処理というより、条件によって処理内容が変わる選択処理に向いている制御文です。プログラミング言語によっては判断・分岐命令と呼ばれることもあります。IF ~ END IF制御文の使い方の基本はこうなります。
IF 条件式 THEN
条件式が成立したときに実行される命令
ELSE
条件式が成立しなかったときに実行される命令
END IF
ここで、条件が成立しなかった時に行なう処理が何もなければ、ELSE以下、END IFの手前までは省略できます(上のサンプルプログラムがそうです)。
さらに条件を細かく分けたいときには、このような使い方もできます。
IF 条件式1 THEN
条件式1が成立したときに実行される命令
ELSEIF 条件式2 THEN
条件式1が成立しないが、条件式2は成立したときに実行される命令
ELSE
条件式1も条件式2も成立しなかったときに実行される命令
END IF
これは少々わかりにくいと思いますので、実例でみてみましょう。次のプログラムでは入力された値が1か2か、それ以外かによって、処理の内容が変わっています。
PRINT "サービスデーのご案内"
INPUT "お客様の性別は 1.男性 2.女性",SEI
IF SEI=1 THEN
PRINT "毎週火曜日は男性のお客様は¥1000で食べ放題"
ELSEIF SEI=2 THEN
PRINT "毎週木曜日は女性のお客様は¥1000で食べ放題"
ELSE
PRINT "毎週水曜日は¥1000で食べ放題"
END IF
PRINT
PRINT "焼肉 〇〇"
END
入力内容が1か2か、それ以外かで処理の流れがどのようになるか(この例では画面に表示される内容)、考えてみてください。実行結果は次のようになります。
条件によって処理内容が選択される
こんな焼肉屋さんがあったら、火・水曜日はいつもここで夕食ですなぁ(^ ^;
[この章のまとめ]
条件によって処理内容が変わる処理を選択処理という。
Tiny BASICでは選択処理に向いている制御文はIF ~ END IF制御文である。基本的な書き方は
IF 条件式 THEN
条件式が成立したときの処理内容
END IF
である。さらに条件を細かく分けたいときは
IF 条件式1 THEN
条件式1が成立したときの処理内容
ELSEIF 条件式2 THEN
条件式1は成立しないが、条件式2が成立したときの処理内容
END IF
【演習6】ユーザー認証のプログラムを作ってみましょう。登録ユーザーは2人で、登録した名前で認証するものとします(ここでは変数で入れておくことにしておきます)。ユーザー名を入力し、登録ユーザー名のどちらかと一致すれば「〇〇(登録ユーザー名)さん、こんにちは」どちらとも一致しなければ「認証できませんでした」と表示するようにすること。このプログラムはいろいろな方法があるのですが、習熟のため、IF制御文を使うようにしましょう。
話を本題に戻します。回数が決まってないとはいえ、データの件数が無限ということはそうそうあることではありません。テストの平均点を求めるプログラムなら受験者の点数すべて入力し終わったらループは終了で、平均の計算に移ります。では、何人受験したかわからない学力テストの平均点を求めるプログラムで「ここでデータ入力終わり」ということをどうやってプログラムに知らせたらよいでしょう。これにはいくつかの方法がありますが、よく使われる方法は、「あり得ないデータ」が入ってきたところでループから抜けるというものです。たとえば
1)テストの点数は0点~100点の範囲である
2)点数に999が入力されたらデータ入力のループを抜ける
3)児童の出席番号(または受験番号)は1、2、3、・・・、Nとなっていて欠番はないものとする。
という前提で、不定回数の繰り返し処理によるテストの平均を求めるアルゴリズムを考えてみます。基本的な流れは次のようになります。
図10_1.受験者人数が一定でない学力テストの平均点を求める
上の図で、入力された点数が(たとえば)75のときと999のときでは処理の流れがどう変わるかを辿ってみてください。このアルゴリズムをTiny BASICでコーディング(プログラミング言語を使って実際にプログラムを作成すること)すると次のようになります。
1: PRINT "***テストの平均点***"
2: GOKEI=0
3: NINZU=0
4: BANGO=0
5: TENSU=-1
6: WHILE TENSU<>999
7: BANGO=BANGO+1
8: PRINT BANGO;"番";
9: INPUT "点数(999=END)",TENSU
10: IF TENSU<>999 THEN
11: GOKEI=GOKEI+TENSU
12: NINZU=NINZU+1
13: END IF
14: WEND
15: HEIKIN=GOKEI/NINZU
16: PRINT "平均点は";HEIKIN
17: END
1~3行目はもう説明不要ですね。
4行目の変数BANGOは出席番号です。え?これ、NINZUを使えばいいんじゃない?と思われる方もいると思いますが、それはあまり感心しません。今ここでは番号の欠番はないものとしていますが、たとえば学校の場合、転校などによっていなくなった児童がいたりします。筆者の知る限りではこういうときその児童の出席番号は欠番となります(学年が変わってクラス替えがない限り1つずつ出席番号が前へずれるということはありません)。塾・予備校などの学力テストなら受験申し込みはしたものの受験はしなかったという児童・生徒がいるかもしれません。そういうとき、データ件数の値と出席番号(受験番号)の値が一致しなくなることは容易に想像がつきます。そのようなことにも対処できるようなプログラムにするにはデータ件数と出席番号は別々の変数にした方が後でプログラムの修正が楽になります。
実際プロのプログラマが作ったプログラムを拝見すると後から機能の追加の要求がきたり、処理内容の一部に変更があってプログラムを修正(メンテナンス)する必要が出てきたときのことを想定して書かれていることがうかがえます。
5行目はループに入るために、その条件となる変数TENSUに「得点の範囲外でループ終了条件とも違う値」を代入しています。WHILEの後ろの条件はループ実行条件であることを思い出してください。
7~9行目、これで点数入力のループに入ったのですから出席番号を +1 して、点数入力です。
10行目がこの章の主役、IF文です。ここでは「変数TENSUの値が999でないときはTHEN以下の処理をせよ」ということになります。どこまでかを示すのが13行目にあるEND IFです(詳細は次節で)。
繰り返し処理はここまでなので14行目にはWENDがあり、6行目のループ実行判定に戻ります。もし今、入力した内容がループ終了条件の999だったら、11~12行目の処理は行なわずに6行目に戻ってくることになります。ここでTENSUの値は999ですから、ループ実行条件は成立せず(ループ終了条件が成立し)、15行目以下に飛びます。平均を計算し、表示してプログラムは終わります。
[IF ~ END IF制御文]
このプログラム中に IF~END IFというのが出てきます。勘の良い方なら「ん?この構文は?FOR ~ NEXTやWHILE ~ WENDのように条件によってプログラムの実行順序を変える制御文かな?」と思ったことでしょう。その通りです。ただ、IF ~ END IFは繰り返し処理というより、条件によって処理内容が変わる選択処理に向いている制御文です。プログラミング言語によっては判断・分岐命令と呼ばれることもあります。IF ~ END IF制御文の使い方の基本はこうなります。
IF 条件式 THEN
条件式が成立したときに実行される命令
ELSE
条件式が成立しなかったときに実行される命令
END IF
ここで、条件が成立しなかった時に行なう処理が何もなければ、ELSE以下、END IFの手前までは省略できます(上のサンプルプログラムがそうです)。
さらに条件を細かく分けたいときには、このような使い方もできます。
IF 条件式1 THEN
条件式1が成立したときに実行される命令
ELSEIF 条件式2 THEN
条件式1が成立しないが、条件式2は成立したときに実行される命令
ELSE
条件式1も条件式2も成立しなかったときに実行される命令
END IF
これは少々わかりにくいと思いますので、実例でみてみましょう。次のプログラムでは入力された値が1か2か、それ以外かによって、処理の内容が変わっています。
PRINT "サービスデーのご案内"
INPUT "お客様の性別は 1.男性 2.女性",SEI
IF SEI=1 THEN
PRINT "毎週火曜日は男性のお客様は¥1000で食べ放題"
ELSEIF SEI=2 THEN
PRINT "毎週木曜日は女性のお客様は¥1000で食べ放題"
ELSE
PRINT "毎週水曜日は¥1000で食べ放題"
END IF
PRINT "焼肉 〇〇"
END
入力内容が1か2か、それ以外かで処理の流れがどのようになるか(この例では画面に表示される内容)、考えてみてください。実行結果は次のようになります。
条件によって処理内容が選択される
こんな焼肉屋さんがあったら、火・水曜日はいつもここで夕食ですなぁ(^ ^;
[この章のまとめ]
条件によって処理内容が変わる処理を選択処理という。
Tiny BASICでは選択処理に向いている制御文はIF ~ END IF制御文である。基本的な書き方は
IF 条件式 THEN
条件式が成立したときの処理内容
END IF
である。さらに条件を細かく分けたいときは
IF 条件式1 THEN
条件式1が成立したときの処理内容
ELSEIF 条件式2 THEN
条件式1は成立しないが、条件式2が成立したときの処理内容
END IF
【演習6】ユーザー認証のプログラムを作ってみましょう。登録ユーザーは2人で、登録した名前で認証するものとします(ここでは変数で入れておくことにしておきます)。ユーザー名を入力し、登録ユーザー名のどちらかと一致すれば「〇〇(登録ユーザー名)さん、こんにちは」どちらとも一致しなければ「認証できませんでした」と表示するようにすること。このプログラムはいろいろな方法があるのですが、習熟のため、IF制御文を使うようにしましょう。
【第9章】繰り返し処理(2)
2020.03.28
[回数が決まっていない繰り返し]
第7~8章でやった繰り返し処理はループの回数が決まっていました(5回の例で説明しました)。これがもし回数が増えたらどうしましょうか。たとえばテストを受けた児童の人数が10人、20人・・・100人と増えたら?この場合は
SEIKAI$="東京"
ANS$=""
WHILE ANS$<>SEIKAI$
INPUT "日本の主都を漢字で書くと";ANS$
WEND
END
まず、3行目のWHILEなんちゃらと5行目のWENDから説明します。WHILEは「~の間」という意味ですね。何となく想像つくと思いますが、これは「~である間、以下の処理を繰り返せ」という意味です。「どこまでを」を指示しているのが WEND です。このWHILE ~ WENDもFOR ~ NEXTと同じくプログラムの実行順序をコントロールする制御文です。WHILEの後ろに来るのは繰り返し実行条件です。ここで<>というのは≠の意味です(比較演算子と呼ばれるもののひとつです)。つまりこの3行目は「変数ANS$の内容が変数SEIKAI$の内容と等しくない間,以下の処理を実行せよ」という意味になります。
Tiny BASICだけでなくほとんどのプログラミング言語では≠が使えません。多くのBASICでは<>、サーバープログラム向きのperlでは!=、昔懐かしいFORTRANでは.NE.などとなっています。
図9_1.クイズのプログラム実行結果
このプログラムを実行すると図9_1のようになります。問題文はINPUTに続くストリングで表示されます。不正解だともう一度答えの入力に戻り、正解が出るとWENDの次に進みます。答えの入力に繰り返す回数は決まっていません。正解するまで時間無制限のデスマッチです。このプログラムではWENDの次には何もありませんから、正解が出ればこれで実行終了です。このように繰り返し回数が決まっていないループでは FOR ~ NEXT制御文よりWHILE~WEND制御文の方が適していることがわかります。
[WHILE ~ WEND 制御文の文法と使用上の注意]
WHILE文の書き方をまとめると次のようになります。
WHILE 条件式
繰り返して行う処理
WEND
WHILEとWENDは必ず対になっていなくてはなりません。
WHILEの後ろに書かれる条件式は、繰り返し(ループ)に入るための条件です。繰り返し処理にWHILE ~ WEND を使う場合、繰り返しを終了する条件とは反対になります。
上の例なら、入力内容が正解でなければループに入れ(終了条件からみれば、入力内容が正解だったらループを抜けよ)ということになります。プログラミングの際に注意を要するところです。筆者はプログラム初心者だった頃、これがよくわかっていなくて1本のプログラムのデバッグで徹夜したことがあります。
[条件式と比較演算子]
条件式は繰り返し処理の他、条件によって処理内容を選ぶ選択処理でも出てくるものですから、ここで書き方と意味をしっかり覚えましょう。条件式の書き方は
となります。式?と思われた方、プログラミングの世界では変数1個、数値1つ、ストリング1つでも式であることを思い出してください。条件式は2つの式を演算子で結んだ形で書かれます。このときに使われる演算子を比較演算子といいます。Tiny BASICで使える比較演算子は次の通りです(下の表では見やすくするためにすべて全角で書きましたが、実際にプログラム中で使うときには半角です)
図9_2.Tiny BASICで使用できる比較演算子
= はここまでずっと代入演算子として使われてきました。代入演算子としての = は「右辺の値を左辺に代入する」でしたから、左辺は変数1つに限定されました。A=5 のような書き方はOKですが、5=Aのような書き方はだめでした。条件式としての = は数学の等号と同じ意味なので、たとえば 5+3=A といった書き方ができます。
以下の2節(【この章のまとめ】の手前まで)は、先をお急ぎの方(とにかくプログラミングをひととおり覚えたい方)は今は飛ばしていただいてもかまいません(以後の実習には支障ありません)。お時間に余裕が出来たら戻ってきてください。
[式の値と論理値]
今まであまり意識してきませんでしたが、プログラミング言語では式は何らかの値を持ちます。まず簡単なものから。
5+3
このような算術式なら、この式の値は計算結果です。この例なら8です。
A=5+3
は、右辺の式の値(8)を左辺(変数A)に代入するということです。では、たった今覚えた条件式の場合、式の値はどうなるかというと、条件が成立したときは真(true)、成立しないときは偽(false)となります。上のプログラムでは、答えに"東京"が入力されると、SEIKAI$=ANS$は真、SEIKAI$<>ANS$は偽(図9_3)となり、ループを抜けるというわけです。WHILE ~ WENDでループを作るときは「繰り返し終了条件が偽である間、以下の処理を繰り返せ」ということになるのでプログラムの書き方としては図9_4のようになります。この真(true)と偽(false)のことをboolean(ブーリアン)値または論理値といいます。
図9_3.終了条件からみた場合
図9_4.繰り返し条件からみた場合
[文字コード]
このサンプルプログラムでは
SEIKAI$="東京"
ANS$=""
WHILE ANS$<>SEIKAI$
ですから、2つのストリングが等しいか等しくないかを判定していることになります。数値ではない文字が等しいかどうか、どうやって判定するのかという話です。
コンピュータには0と1の2進数しかわからない、という話をしましたが、これはプログラムだけでなく、データも同じです。たとえば8という数を8桁の2進数で表すと、00001000となります。記憶するときにもこの2進数で記憶されます。では文字の記憶はどうするのでしょう。文字もやはり数値に直して記憶されます。文字を数値に直したものを文字コードといいます。ストリングどうしが等しいか等しくないかを判定するのはこの文字コード(数値)が等しいか等しくないかを判定しているのです。
何の文字をいくつにするのかは、いろいろな規格があるのですが、現在パソコンで広く使われているものはアスキーコードとよばれる規格です。アスキーコードでは半角の"A"は65(10進)、半角の"a"は97(10進)としています。図9_5は半角A~Z、a~zのアスキーコードを示したものです。この表では文字コードは10進で示してあります。
図9_5.アルファベット26文字のアスキーコード
[この章のまとめ]
不定回の繰り返し処理では、WHILE ~ WEND制御文を使う。
WHILE文の書き方は
繰り返し実行条件が成立する時は繰り返し終了条件は成立しない。繰り返し終了条件が成立するときは繰り返し実行条件は成立しない。
条件式の書き方は
比較演算子としての=は代入命令ではなく、数学の等号と同じ意味を持つ。
[演習5]
WHILE ~ WEND制御文を使って、クイズの問題を作りなさい。答えはキーボードから入力するものとします。問題文はPRINT命令で実行画面に表示/INPUT ストリングで入力窓に表示、どちらでもけっこうです。正解ではなかったら「もう一度!」と表示、正解だったら「正解です」と正解するまでにかかった回数を表示して終わるようにすること。問題が思いつかない人は下記の問題例を使ってください。( )内が正解です。
(問題例)
ベートーヴェンの交響曲第5番は通称何と呼ばれていますか(運命)
ポークは豚、ビーフは牛、チキンは鶏、ではターキーは(七面鳥)
明治5年、日本で最初に開通した鉄道は東京からどこまで(横浜)