【第12章】データの保存と再利用(2)データをブログラム中に置く

 前章のプログラムは出席番号、氏名、テストの得点すべてをキーボードから入力するものでした。作ったプログラムを実行してみていかがでしたか。データ入力がかなりたいへんだと感じた方も多いのではないでしょうか。小学校の先生なら(原則的には)すべての教科を担任の先生が教えます。中学・高校の先生なら担当教科は一つでも同じ学年の複数のクラスを担当することも多いでしょう。その度に出席番号、氏名、得点を入力することになり、入力が多くなればなるほど誤入力(誤操作)が発生する確率も高くなります。これをなんとかしましょう。

      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


              図12-1.DATA文の内容をREAD命令で読み込むプログラム


[プログラム中にデータを入れる]


 図12-1
のプログラムを見てください。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-2.DATA文の内容をREAD命令で読み込む
図12-2.DATA文の内容をREAD命令で読み込む


 DATA文で入れるデータはデータの型、読み込む変数の数が合っていればプログラム中のどこへ置いてもかまいません。またTiny BASICではDATA文は制御文ブロックの中に置いてもかまいません。DATA文は繰り返し処理や選択処理の対象にはなりません。図12-3のプログラムも図12-1のプログラムと同じ動作をします。

   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


                          図12-3.DATA文を制御文ブロックの中に置く


 しかし、プログラム上は正しく動作するといっても、このようなプログラムはわかりにくいことこの上ないですね。後でプログラムの修正が必要になったとき作った当人でもかなり苦労すると思います。プログラムは後々のことを考えてわかりやすく書きましょう。

[固定的なデータをプログラムの中に置く]

 このREAD、DATA文を使って第11章の図11-4のプログラムを改良しましょう。前章のプログラムでの問題点は出席番号、氏名、試験の点数すべてをキーボードから入力することでした。ここでこのプログラムで扱うデータの「性格」を考えてみましょう。出席番号、氏名といったデータは普通に考えれば学年の途中で全員分が変わるということはそうそうあることではありません。このような固定的なデータをマスターといいます。これに対し、試験の点数は変動的なデータ(トランザクション、元々は取引、売買の意味)です(筆者の体育の成績は例外です。5段階評価で3以下しか取れたことありません💦)。
 効率良さを考えるならマスターデータはDATA文でプログラム中に置き、これをREAD命令で読み込みながらトランザクションデータをキーボードから入力するというのが一般的です。プログラムは図12-4のようになります。

      1:DIM BANGO$(100),NAMAE$(100),TENSU(100)            ' データ保存領域
      2:RESTORE *CLASS_MEIBO              ' READで読み込むデータの開始位置を指定
      3:PRINT "***テストの平均点***"
      4:GOKEI=0
      5:NINZU=0
      6:I=0
      7:
      8:  NUM$="-1"
      9:WHILE NUM$<>"999"   ' 出席番号が終端値ではない間、以下の処理を繰り返す
    10:    READ NUM$,SHIMEI$ ' クラス名簿のデータを1件ずつ読み込む
    11:    IF NUM$<>"999" THEN ' 終端マークでなければ以下の処理を行なう
    12:          PRINT NUM$;" ";SHIMEI$;
    13:          INPUT "点数 ",TOKUTEN
    14:          BANGO$(I)=NUM$
    15:          NAMAE$(I)=SHIMEI$
    16:          TENSU(I)=TOKUTEN
    17:          GOKEI=GOKEI+TOKUTEN
    18:          NINZU=NINZU+1
    19:          I=I+1
    20:    END IF                          ' 出席番号が終端マークでないときの処理ここまで
    21:WEND                            ' 出席番号が終端値ではない間の繰り返しここまで
    22:LASTNUM=I-1
    23:
    24: PRINT
    25:' 平均点と、各自の得点と平均点の差を求めて表示
    26:HEIKIN=INT(GOKEI/NINZU)
    27:PRINT USING "受験者は ###人";NINZU
    28:PRINT USING "平均点は ###点";HEIKIN
    29:PRINT
    30:' 各自の得点と平均点との差を求める
    31:FOR I=0 TO LASTNUM
    32:    TENSA=TENSU(I)-HEIKIN
    33:    PRINT BANGO$(I);" ";NAMAE$(I);TAB(20);     ' 出席番号、氏名を表示
    34:    PRINT USING "### (###)";TENSU(I);TENSA ' 得点と平均点との差を表示
    35:NEXT I
    36:END
    37:
    38:' クラスの名簿(出席番号と氏名)
    39:*CLASS_MEIBO
    40:DATA "A001","青木 郁夫"
    41:DATA "A002","岩本 浩司"
    42:DATA "A003","大田 喜美子"
    43:DATA "A004","加藤 正孝"
    44:DATA "A005","山口 小夜子"
    45:DATA "B001","江藤 今日子"
    46:DATA "B002","近藤 仁"
    47:DATA "B003","中田 真一"
    48:DATA "B004","森田 淳二"
    49:DATA "B005","渡辺 三郎"
    50:DATA "999",""            ' 終端マーク

           図12-4.マスターデータをプログラム中に置く


 今回はプログラムを見やすく、また処理結果を見やすくを意識してみました。
 まずプログラム中のあちこちに出てくる ' (アポストロフィー)に続くなんたらかんたらですが、これは注釈です。Tiny Basicでは半角の ' は引用符ではなく、その後から行の終わりまでが注釈であることを示します。行の先頭に ' を付けると1行全部が注釈行になります。注釈はプログラム実行の際、無視されますから、通常のキーボードから入力できる文字なら何でも書けます。アラビア文字、ハングル文字、キリル文字などもOKですが、プログラム保存の際、エンコーディングをUTF-8にする必要があります。後にプログラムの修正等が必要になったとき、この変数は何のため?ここでどういう処理やってるの?などをわかりやすくしておくため注釈はどんどん入れましょう。
 7、23、37行目は処理のかたまりをわかりやすくするために置いた空行です。空行を置くときは全角の空白(スペース)が入ってしまわないように気をつけてください(エラーになります。修正の際、探すのがけっこう大変です)。
24、29行目のPRINT(オペランドなし)は、実行画面上で1行空行を空けて画面を見やすくするためのものです。
 27、28行目のPRINT USINGは続く" "内で指定した書式にしたがってデータを表示するものでした(第7章の後ろの方でやりました)。" "内に書式設定子以外の文字を入れると、その文字はそのまま表示されます。ここでは受験者数と平均点の1の位を合わせて見やすくするために使っています。
 33行目に出てくるTABは、tabulatorの意味で昔、タイプライターを使ったことのある方なら、ああ、あれか、とピンときたと思います。TABは行の左端から( )内で指定した位置まで表示位置を移動するものです。表示位置を移動するだけで空白を埋めるわけではないので、PRINT USINGと併用すると各項目の位置を揃えて見やすく表示させることができます。
 38~50行目は10行目のREAD命令で読み込んでゆくデータ(今回の例ではクラス名簿マスタデータ)です。ここで10行目は
     READ ストリング変数1,ストリング変数2
なのでデータの並びはこれに従っていれば、たとえば
     DATA "A001","青木 郁夫"
     DATA "A002","岩本 浩司"
     DATA "A003","大田 喜美子"
と書くところを
     DATA "A001","青木 郁夫","A002"
     DATA "岩本 浩司","A003","大田 喜美子"
と書いてもTiny Basicの文法的には問題ないのですが、プログラムとデータの関係がわかりにくくなってしまいます。メモリ節約との戦いだった1980年代頃ならともかく、今のパソコンとTiny Basicでのプログラミングでは、メモリ節約よりプログラムがわかりやすい書き方でいきましょう。
 50行目はデータの終端です。出席番号が"//"だったらデータ読み込み終了なので、出席番号にあたるデータを // にしています。その後の "" はNULL(ヌルストリング)といい、何も文字がないストリングです。10行目のREAD命令は出席番号と氏名を読み込むようになっているので、データの終端でも氏名のデータがないとエラーになります。そこで氏名の部分にダミーのデータを置いておく必要があるのです。
 このプログラムの実行結果は図12-5のようになります。

図12-5.図12-4のプログラムの実行結果
図12-5.図12-4のプログラムの実行結果


 このようにマスタデータはDATA文でプログラム中に置いてREAD命令で読み込んでゆくようにすると入力が楽になると同時に入力ミスを防ぐことができます。またマスタデータに変動が起こったとき、DATA文の部分を追加・削除・修正すれば済むことになり、システム全体のメンテナンス性も向上します。

【昔はよかったなぁ】

 昔のBASICを使って、図12-4のプログラムを平均点以下の受験者を黄色で、さらに平均点より20点以上低い受験者を赤で表示する鬼畜なプログラムにしてみましょう(図12-6)。

図12-6.平均点より低い受験者を色付きで表示する
図12-6.平均点より低い受験者を色付きで表示する

 プログラムは図12-7のようになります。昔のBASICは文字に色を付けるのは簡単でした。行番号380に出てくるCOLOR命令がそれです。
 行番号200にGOTOという命令がありますが、これは無条件ジャンブといい、指定された行番号(またはラベル)まで処理の手順を飛ばすものです。今のTiny Basicにもあることはあります。ただしGOTO命令をむやみに使うとプログラムがわかりにくくなります。IT関連企業の中にはGO TO(GOTO)命令の使用を禁止している会社もあるくらいです。ただ、昔のBASICではどうしても使わざるを得ないケースがありました(第14章の【昔はよかったなぁ】に出てきます)。
 もうひとつ、DATA文のところを見てください。昔のBASICではDATA文に含まれるストリングは" "で囲む必要はありませんでした。昔のBASICではDATA文群はかなりすっきりと書けました。

図12-7.図12-4のプログラムの実行結果
図12-7.平均点より低い受験者を色付きで表示するプログラム


【この章のまとめ】

DATA文でプログラム中にデータを置くことができる。

DATA文で置いたプログラムはREAD命令で変数に読み込むことができる。

RESTORE文でREAD文で読み込むDATA文がどこからかを指示できる。

マスタデータはDATA文でプログラム中に置くようにすると、入力が楽になり、入力ミスも防ぐことができる。

マスタデータはDATA文で置くようにするとマスタデータのメンテナンス性が向上する。

PRINT USING" "とTAB( )を上手に組み合わせると処理結果を見やすく表示させることができる。

ピアノ超々入門



このコンテンツは、ピアノに触れるのが全く初めて、という方のためのものです。
富山大学在職中に、人間発達科学部の「音楽科教育論」(2020年度)の授業補助用として立ち上げました。

[当時は受講生にこのように指示していました。参考までに]

 過去にピアノ等の鍵盤楽器を習ったことのない方は【だれでもピアノ】から、過去にある程度までピアノ・電子オルガン等を習ったことはあるものの中断期間が数年ある方は第0章から、現在もピアノを習っている方、演奏に自信がある方は第3章から始めてください。

  (注)これより先の各ページからこのページへ戻るリンクはありません。戻るときはブラウザのバックボタンをご利用ください。

【だれでもピアノ】第1回なにか1曲弾いてみよう
【だれでもピアノ】第2回レパートリーを増やそう
【だれでもピアノ】第3回弾ける音を増やそう
【第0章】基礎知識
【第1章】指を動かす練習
【第2章】左右の手で違うことをする練習
【第3章】「かたつむり」の伴奏を弾いてみよう
【第4章】「ゆうやけこやけ」の伴奏を弾いてみよう
【質疑応答】相対的音感

HOME

【第11章】データの保存と再利用(1)配列を使う

 繰り返し処理、選択処理では、まだまだお話したいことがたくさんあるのですが、このカテゴリーの目的には1週間~10日ほどでプログラミングをひととおりやってみましょうということもあるので、とりあえず今は先に進みます。
 コンピュータのコンピュータらしいところというと、計算が速い、たくさんのデータを記憶できるなどいろいろありますが、もうひとつ「データの保存・再利用ができる」ということがあります。電卓にもメモリーキーがありますが、コンピュータの場合、記憶・保存できるデータが数値だけでなくストリングも可能です。この章からしばらくいろいろなデータの保存・再利用の仕方について勉強します。この章では配列を使ってデータを保存することを覚えましょう。
 ここまでテストの平均点を求めるプログラムを例にやってきましたが、これをもう少し改造してみましょう。平均点が出たところで、補習対象者(仮に平均点の2分の1に達しなかった生徒とします・・・厳し過ぎますか?)を抽出して表示するようにします。どうしたらよいでしょう。この場合、まず全受験者の点数の平均を求めてから、それぞれの点数が平均の2分の1以上・未満を判定することになります。そのためには最初に入力した全員の点数を保存しておく必要があります。しかし、しか~し、変数はたしかにデータを記憶できるのですが、それは次に同じ変数に別のデータが入ってくるまでの間です。たとえば、次のようなプログラムでは①で5を入力したとするとこの時点で変数Aには5が入っていますが②で10を入力したとすると、この時点で最初に入力した5は消えてしまいます。

図11-1.変数に新しい値が入ると前の値は追い出される
図11-1.変数に新しい値が入ると前の値は追い出される

 これでは平均を求めた後、このデータを再利用できません。最初に入っていた値を残しておくにはどうしたらよいでしょう。


[配列]
図11-2.配列の概念
図11-2.配列の概念


 ここにハイツ立山というアパートがあります。1号室は田中さん、2号室は青木さん、3号室は木下さん、4号室は空室、5号室は渡辺さんです。これをTiny BASICっぽく書くと
NAMAE$=TATEYAMA$(1)で変数NAMAE$には"田中"と入ります。同じようにNAMAE$=TATEYAMA$(2)とすると変数NAMAE$は"青木"となります。またTATEYAMA$(4)="高橋"とすると4号室には高橋さんが入居します。配列とはこのようなものです。配列を使うには使用前にDIM(dimension、寸法、次元の意味)で使用宣言をする必要があります。宣言の方法はこうなります。

   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では配列変数と呼んでいます)。たとえば次のプログラムを実行すると図11-3.のようになります。

  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.配列の概念
図11-3.入力したデータを配列に保存する


 これでキーボードから入力したデータは配列に保存されます。ただしこのような使い方では配列を使う意味はほとんどありません。配列を使う大きなメリットは繰り返し処理の中で同じ処理で異なる変数が扱えることです。これを使って前章でやったプログラムを図11-4のように改造してみましょう。このプログラムはテストの平均点を求めた後、それぞれの生徒の得点が平均とどれくらい差があるかを表示するものです。得点-平均点で、平均点より高ければ正、低ければ負となります。入力したデータは配列に保存しておき、平均点を求めた後に再利用します。

     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

    図11-4.テストの平均点と受験者各々の点数の差を求めるプログラム

 前章のプログラムに追加・変更された部分について説明します。
 1行目は配列の宣言です。ここでは番号、氏名、得点を保存しておく配列を0から50で51個ずつ(51人分)用意しています。
 6行目で配列の添え字用の変数の初期値を 0 としておきます。
 今回は出席番号はストリング型としています。出席番号は普通計算の対象にはならないのと、出席番号に数字以外の文字が含まれる場合(1年A組1番が 1A01 など)にも対応するためです。そして出席番号は手動で入力することにします。出席番号に欠番があったときの対策です。8行目以下のWHILEブロックに入るため7行目では出席番号にあり得ないデータとして -1 を代入しておきます。
 9行目で出席番号を入力します。ここで"999"が入力されたらデータ入力終了です。
 出席番号が"999"でなければまだデータ入力は続きますから、氏名、点数を入れるのは11~12行目(WHILE~WENDのループ内)になります。

 13~15行目は入力したデータを配列に保存しておくところです。このように配列の添え字には変数も使えます(変数の中身は0以上の整数でなくてはなりません)。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-5.実行画面
図11-5.実行画面


【昔はよかったなぁ】

図11-4のプログラムを昔のBASICで書くと図11-6のようになります。

図11-6.実行画面
図11-6.図11-4のプログラムを昔のBASICで書くと


 行番号10に
    OPTION BASE 1
とありますが、これは配列添え字の下限を 0 にするか 1 にするかの指定です。命令というか宣言です。どうして昔のBASICにはこんなものがあったのかというと、この宣言がないとこのプログラム中のすべての配列の添え字は 0 からになります。しかし、プログラム中で使うすべての配列の添え字の最小値が 1 だとした場合、各配列の添え字 0 の要素が主記憶装置上に占める分は無駄になります。OPTION BASE 1とすると、プログラム中の全配列の添え字 0 の要素がなくなるので、それだけ主記憶装置上に占める分の節約になります。昔のBASICが全盛だった頃のパソコンは主記憶装置の容量が8ビットパソコンで64KB、16ビットパソコンで640KBが最大でしたから、実務プログラムを作ろうとすると、このようにして(これ以外の方法でも)メモリ節約との戦いになったものです。
 今日のパソコンでは主記憶装置の容量が4GBとか大容量になりましたから、普通の事務処理プログラムやゲームプログラムではメモリ節約なんて考えなくともよくなったので、今のTiny BasicにはOPTION BASEはありません。
 行番号にGOTOというのがあります。これは無条件ジャンプといい、指定された行番号(またはラベル)以後の処理にジャンブする命令です。制御ブロックがなかった(構造化プログラミングがほぼ不可能な)昔のBASICにはなくてはならない命令でした。ただGOTO命令の使い過ぎはプログラムがわかりにくくなる原因にもなり、当時からあまり推奨される命令ではありませんでした。今のTiny BasicでもGOTO命令はあるのですが、制御ブロックが使える(構造化プログラミングが可能)ので、よほどの場合でない限り、GOTO命令は出番がなくなりました。

【この章のまとめ】

変数名の後ろに(数字)が付いたものを配列(配列変数)という。
配列を使うとデータの保存、再利用ができる。
配列を使用するには DIMステートメントを使う。書き方は
   DIM 配列名(正の整数)
配列名の規則は変数名と同じである。正の整数は配列のサイズ(添え字の最大値)である。添え字の最小値は 0 である。

INT(数値)は( )内の数値を整数化(小数点第1位以下を切り捨てる)する。関数のひとつである。

【演習7】次のプログラム仕様書に基づくプログラムを作りなさい。

演習7


【第10章】平均を求めるプログラム(2)選択処理


[回数が決まっていない繰り返し処理]

 不定回の繰り返し処理のやり方はおわかりいただけたでしょうか。回数が決まってないとはいえ、データの件数が無限ということはそうそうあることではありません。テストの平均点を求めるプログラムなら受験者の点数すべて入力し終わったらループは終了で、平均の計算に移ります。では、何人受験したかわからない学力テストの平均点を求めるプログラムではどうしたらよいでしょう。この場合、よく使われる方法は、「あり得ないデータ」が入ってきたところでループから抜けるというものです。この章ではさらに「条件によって処理内容を変える」という方法を学びます。コンピュータの最もコンピュータらしい部分で、これができるゆえに、筆者が小学生だった昭和40年頃は、コンピュータは「人工頭脳」と呼ばれたりしたものでした。今でも中国語では「電脳」といい、日本でも電脳少女など、ホビーの世界ではこの言い方が使われてますね。
 歳とると昔話が多くなってダメですね。話を元に戻します。たとえば

1)テストの点数は0点~100点の範囲である
2)点数に999が入力されたらデータ入力のループを抜ける
3)児童の出席番号(または受験番号)は1、2、3、・・・、Nとなっていて欠番はないものとする。

という前提で、不定回数の繰り返し処理によるテストの平均を求めるアルゴリズムを考えてみます。基本的な流れは次のようになります。


図10_1.受験者人数が一定でない学力テストの平均点を求める図10_1.受験者人数が一定でない学力テストの平均点を求める
図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

      図10_3.受験者人数が一定でない学力テストの平均点を求めるプログラム

 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~END IFというのが出てきます。勘の良い方なら「ん?この構文は?FOR ~ NEXTやWHILE ~ WENDのように条件によってプログラムの実行順序を変える制御文かな?」と思ったことでしょう。その通りです。ただ、IF ~ END IFは繰り返し処理というより、条件によって処理内容が変わる選択処理に向いている制御文です。プログラミング言語によっては判断・分岐命令と呼ばれることもあります。
1)IF ~ END IF制御文の使い方の基本はこうなります。
  IF 条件式 THEN
    条件式が成立したときに実行される命令
  ELSE
    条件式が成立しなかったときに実行される命令
  END IF
ここで、条件が成立しなかった時に行なう処理が何もなければ、ELSE以下、END IFの手前までは省略できます(上のサンプルプログラムがそうです)。
2)さらに条件を細かく分けたいときには、このような使い方もできます。

  IF 条件式1 THEN
    条件式1が成立したときに実行される命令
  ELSEIF 条件式2 THEN
    条件式1が成立しないが、条件式2は成立したときに実行される命令
  ELSE
    条件式1も条件式2も成立しなかったときに実行される命令
  END IF

 これは少々わかりにくいと思いますので、実例でみてみましょう。図10-3のプログラムでは入力された値が1か2か、それ以外かによって、処理の内容が変わっています。


    PRINT "サービスデーのご案内"
    INPUT "お客様の性別は 1.男性 2.女性 3.無回答 ",SEI
    IF SEI=1 THEN
      PRINT "毎週火曜日は男性のお客様は¥1000で食べ放題"
    ELSEIF SEI=2 THEN
         PRINT "毎週木曜日は女性のお客様は¥1000で食べ放題"
    ELSE
    PRINT "毎週水曜日は¥1000で食べ放題"
    END IF
    PRINT
    PRINT "焼肉 〇〇"
    END


     図10_3.条件によって処理内容が選択されるプログラム

 入力内容が1か2か3かで処理の流れがどのようになるか(この例では画面に表示される内容)、考えてみてください。実行結果は図10-4のようになります。


図10-4.条件によって処理内容が選択される
図10-4.条件によって処理内容が選択される

 こんな焼肉屋さんがあったら、火・水曜日はいつもここで夕食ですなぁ(^ ^;
3)IF~END制御文も、FOR~NEXT制御文、WHILE~WEND制御文同様、ネスティングができます。

     1:SCORE=0
     2:PRINT "歴史クイズ"
     3:INPUT "日本最初の元号は 1.大和 2.大化 3.文化 ",ANS
     4:IF ANS=2 THEN
     5:    SCORE=SCORE+10
     6:    INPUT "徳川第八代将軍は 1.吉宗 2.家光 3.綱吉 ",ANS
     7:    IF ANS=1 THEN
     8:        SCORE=SCORE+10
     9:        INPUT "フランス革命の始まりとされているのは 1.ナポレオン台頭 2.ポーランド分割 3.バスティーユ監獄衝撃",ANS
    10:       IF ANS=3 THEN
    11:          SCORE=SCORE+10
    12:       ELSE
    13:          PRINT "正解は3.バスティーユ監獄襲撃です"
    14:       END IF
    15:   ELSE
    16:       PRINT "正解は1.吉宗です"
    17:   END IF
    18:ELSE
    19:    PRINT "正解は2.大化です"
    20:END IF
    21:PRINT "SCORE=";SCORE
    22:END

     図10-5.正解したら次へ進めるクイズのプログラム

 図10-5は1つでも正解できないと脱落するという(鬼畜な?)クイズゲームです。正解しないと次の問題に進めないというのはIF~ENDIFのネスティングで実現してます。1問目が正解なら5行目以下が実行され、不正解なら19行目へ飛んでプログラム終了です。しかし6~7行目に2問めと解答の判定があります。2問めも正解なら8行目以下が実行され、不正解なら16行目へ飛びます。ここで1問目は正解しているので18~20行目は無視して21行目へ飛び、プログラム終了です。同様に9~10行目に3問めと解答の判定があります。正解なら11行目を、不正解なら13行目を実行してプログラム終了です。

【Tiny BASICの文法(4)】マルチステートメント
 Tiny Basicでは1行に1ステートメントを書くのが普通ですが : (半角のコロン)を使って1行に2つ以上のステートメントを書くことができます(マルチステートメント)。たとえば
       T=0
       INPUT "Nの値は";N
       FOR I=1 TO N
       T=T+I
       NEXT I
       PRINT "1~";N;"の自然数の合計は";T
       END

のプログラムは次のように書いてもTiny Basicの文法としては間違いではありません。

T=0:INPUT "Nの値は";N:FOR I=1 TO N:T=T+I:NEXT I:PRINT "1~";N;"の自然数の合計は";T:END

 ただ、これは読みにくいことこの上ないですね。マルチステートメントはこのようにむやみに使うとプログラムがわかりにくくなる原因になります。昔のBASICはIF制御文はブロック化ができなかったので、このマルチステートメントは絶対必要だったのですが、今のTiny Basicでは必要ないものとなりました。マルチステートメントは使わないようにしましょう。

【昔はよかったなぁ】

 今回は「昔はよかった」ではなく「昔はひどかったなぁ」というお話です。
 図10-3のプログラムを昔のBASICで書くと図10-5のようになります。

図10-5.条件により処理内容が変わるプログラムを昔のBASICで書くと
図10-5.条件により処理内容が変わるプログラムを昔のBASICで書くと

 昔のBASICは END IF がありませんでした。つまりIFブロックは使えませんでした。入力内容により処理内容を変えたいなら行番号30のようにELSEIF(昔のBASICではELSE IF)以下を1ステートメントにしなければなりませんでした。
 また条件が成立した(反対に成立しなかった)時の処理が複数あるとき、それをすべて1ステートメントで書かなくてはならなかったので、図10-3のプログラムの10~13行目の部分を昔のBASICで書くと、
   IF TENSU<>999 THEN GOKEI=GOKEI+TENSU : NINZU=NINZU+1
となります。後でプログラムを見なおした時、自分で作ったプログラムなのに何が何だかわからないということもよくありました。今のTiny BasicはIF制御文もEND IFを使ってFOR制御文、WHILE制御文のようにブロック化し、わかりやすいプログラムが書けるようになりました。

【この章のまとめ】

条件によって処理内容が変わる処理を選択処理という。

Tiny BASICでは選択処理に向いている制御文はIF ~ END IF制御文である。基本的な書き方は

  IF 条件式 THEN
    条件式が成立したときの処理内容
  END IF

である。さらに条件を細かく分けたいときは
  IF 条件式1 THEN
    条件式1が成立したときの処理内容
  ELSEIF 条件式2 THEN
    条件式1は成立しないが、条件式2が成立したときの処理内容
  END IF

【演習6】ユーザー認証のプログラムを作ってみましょう。登録ユーザーは2人で、登録した名前で認証するものとします(ここでは変数で入れておくことにしておきます)。ユーザー名を入力し、登録ユーザー名のどちらかと一致すれば「〇〇(登録ユーザー名)さん、こんにちは」どちらとも一致しなければ「認証できませんでした」と表示するようにすること。このプログラムはいろいろな方法があるのですが、習熟のため、IF制御文を使うようにしましょう。

【ここまでのまとめ】

 ここまでで、プログラムの基本的な流れ「順次処理」「繰り返し処理」「選択処理」についてひととおりやってきました。コンピュータのプログラムはゲームのプログラムから銀行のシステム、人命を預かる新幹線の列車集中制御(CTC)のプログラムまで基本はこの3つの流れの組み合わせでできています。ここまでの内容がわかったら、ご自分のアイデアでプログラムを作ってみてください。そういう経験をたくさん積んでゆけば、プログラミング的思考とは何かがおのずとわかってくることでしょう。

【第9章】繰り返し処理(2)


[回数が決まっていない繰り返し]

第7~8章でやった繰り返し処理はループの回数が決まっていました(5回の例で説明しました)。これがもし回数が増えたらどうしましょうか。たとえばテストを受けた児童の人数が10人、20人・・・100人と増えたら?この場合は


                         FOR ループ変数=初期値 TO 終端値

の終端値を変えれば済むことです。では全国規模の模試のように受験者が何千人、何万人規模で、答案を回収するまで実際に受験した人数か試験が終わってわからないような場合はどうしたらよいでしょう。結論を先に言いますと、FOR ~ NEXT ループは不定回の繰り返し処理にはあまり向きません。では、図9-1のようなクイズのプログラムで、ある条件を満たしている間、繰り返し処理を行なう方法を考えてみます。


図9-1.不定回繰り返し
図9-1.不定回繰り返し


 図9-1の流れに従って作ったプログラムは図9-2のようになります。

            SEIKAI=1
            ANS=0
            WHILE ANS<>SEIKAI
                     PRINT "幻想即興曲、子犬のワルツで知られる作曲家といえば?"
                     PRINT "1. ショパン 2.ショクパン"
                     INPUT "番号で応えてください",ANS
            WEND
            END

                                                    図9-2.クイズのプログラム

 まず、3行目のWHILEなんちゃらと5行目のWENDから説明します。WHILEは「~の間」という意味ですね。何となく想像つくと思いますが、これは「~である間、以下の処理を繰り返せ」という意味です。「どこまでを」を指示しているのが WEND です。このWHILE ~ WENDもFOR ~ NEXTと同じくプログラムの実行順序をコントロールする制御文です。WHILEとWENDで囲まれた部分をWHILEブロックといいます。WHILEの後ろに来るのは繰り返し実行条件です。ここで<>というのは≠の意味です(比較演算子と呼ばれるもののひとつです)。つまりこの3行目は「変数ANS$の内容が変数SEIKAI$の内容と等しくない間,以下の処理を実行せよ」という意味になります。
 Tiny BASICだけでなくほとんどのプログラミング言語では≠が使えません。多くのBASICでは<>(または><)、サーバープログラム向きのperlでは!=、昔懐かしいFORTRANでは.NE.などとなっています。


図9-3.クイズのプログラム実行結果
図9-3.クイズのプログラム実行結果


 このプログラムを実行すると図9-3のようになります。問題文はWHILEブロック内のPRINTで表示、解答入力は同じくWHILEブロック内のINPUTで行ないます。これにより正解を答えられるまで問題文の表示と解答入力に戻り、正解が出るとWENDの次に進みます。答えの入力に繰り返す回数は決まっていません。正解するまで回数無制限のデスマッチです。このプログラムではWENDの次には何もありませんから、正解が出ればこれで実行終了です。このように繰り返し回数が決まっていないループでは FOR ~ NEXT制御文よりWHILE~WEND制御文の方が適していることがわかります。

[WHILE ~ WEND 制御文]

 WHILE文の書き方をまとめると次のようになります。
      WHILE 条件式
        繰り返して行う処理
      WEND

1)WHILEとWENDは必ず対になっていなくてはなりません。
2)WHILEブロックはプログラム中にいくつあってもかまいません。
3)WHILEブロックの中にWHILEブロックを包括することができます(ネスティング)。
WHILEブロックのネスティングを使うと、データの誤入力(誤ったデータを入力すること)防止を施すことができます(この後の章でやります)。

[繰り返し条件]


 WHILEの後ろに書かれる条件式は、繰り返し(ループ)に入るための条件です。繰り返し処理にWHILE ~ WEND を使う場合、繰り返しを終了する条件とは反対になります。
図9-2のプログラム例なら、入力内容が正解でなければループに入れ(終了条件からみれば、入力内容が正解だったらループを抜けよ)ということになります。プログラミングの際に注意を要するところです。筆者はプログラム初心者だった頃、これがよくわかっていなくて1本のプログラムのデバッグで徹夜したことがあります。

[条件式と比較演算子・論理演算子]

 条件式は繰り返し処理の他、条件によって処理内容を選ぶ選択処理でも出てくるものですから、ここで書き方と意味をしっかり覚えましょう。条件式の書き方は

        式1 比較演算子 式2

となります。式?と思われた方、プログラミングの世界では変数1個、数値1つ、ストリング1つでも式であることを思い出してください。条件式は2つの式を演算子で結んだ形で書かれます。このときに使われる演算子を比較演算子といいます。Tiny BASICで使える比較演算子は次の通りです(下の表では見やすくするためにすべて全角で書きましたが、実際にプログラム中で使うときには半角です)

図9-4.Tiny BASICで使用できる比較演算子
図9-4.Tiny BASICで使用できる比較演算子

 = はここまでずっと代入演算子として使われてきました。代入演算子としての = は「右辺の値を左辺に代入する」でしたから、左辺は変数1つに限定されました。A=5 のような書き方はOKですが、5=Aのような書き方はだめでした。条件式としての = は数学の等号と同じ意味なので、たとえば 5+3=A といった書き方ができます。
 このサンプルプログラムには出てきませんが、条件式を書くのにもうひとつ覚えておきたいのは論理演算子です。

図9-5.Tiny BASICで使用できる論理演算子
図9-5.Tiny BASICで使用できる論理演算子


 論理演算子はこの他にもいろいろあるのですが、図9-5の3つはぜひ使えるようにしておきたいというものです。論理演算子の優先順位は、NOT、AND、ORです。ただし( )内が優先されます。

               IF A=B AND (C=D OR NOT E=F) THEN
                   処理
               END IF

  これは
    E=Fが成立しない、またはC=Dが成立し、かつA=Bが成立するとき以下の処理を行なう
ということになります。

[この章のまとめ]

不定回の繰り返し処理では、WHILE ~ WEND制御文を使う。

WHILE文の書き方は

WHILE 繰り返し実行条件

繰り返し実行条件が成立する時は繰り返し終了条件は成立しない。繰り返し終了条件が成立するときは繰り返し実行条件は成立しない。

条件式の書き方は

         式1 比較演算子 式2

         条件式1 論理演算子 条件式2

比較演算子としての=は代入命令ではなく、数学の等号と同じ意味を持つ。

【演習5】

 WHILE ~ WEND制御文を使って、クイズの問題を作りなさい。答えはキーボードから番号で入力するものとします。問題文はPRINT命令で実行画面に表示/INPUT ストリングで入力窓に表示、どちらでもけっこうです。正解だったら「正解です」というメッセージと正解するまでにかかった回数を表示して終わるようにすること。問題が思いつかない人は下記の問題例を使ってください。( )内が正解です。
(問題例)
  ベートーヴェンの交響曲第5番は何と呼ばれていますか(1.英雄 2.運命 3.田園、正解は2)
  ポークは豚、ビーフは牛、チキンは鶏、ではターキーは(1.羊 2.アヒル 3.七面鳥、正解は3)
  明治5年、日本で最初に開通した鉄道は東京からどこまで(1.横浜 2.京都 3.大阪、正解は1) 


- CafeLog -