DHC-オンライン講座
文系の人にもわかる プログラミング入門

Lesson 5   繰り返し

このLessonでは, 前のLessonで学んだ分岐の復習をしながら, 「繰り返し(loop)」について学びます。 条件分岐と同様, 繰り返しもプログラムには欠くことのできない大切な要素です。

計算練習

コンピュータ(computer)の元々の意味は「計算するもの(人)」です。 日本語でもひと昔前までは「電子計算機」あるいはこれを略して「計算機」と呼ばれることもありました。 この名のとおり, コンピュータは「計算」をするのがとても得意です。 今でこそ, ワープロとか翻訳ソフトとか人間の話す言葉(自然言語)を処理するのにも活躍していますが, 最初の頃は爆弾の軌道計算など文字どおりの「計算」が主な用途でした。

さて, 日本の小学校に入ってしばらくすると, 計算の勉強が始まります。 最初は足し算引き算です。 覚えてしまえば何の苦もありませんが, 何も知らないところからやるのは大変で, 何回も何回も繰り返し練習して, ようやく自由に加減算ができるようになります。

この章からしばらくの間, 計算が得意なコンピュータを使って, 小学生向けの計算練習のページを作ってみましょう。 文系の人でも, 足し算引き算ならば, わからないということはないでしょう。

メモ

自分の趣味や研究のために作るような場合を除いて, ほとんどのプログラムはほかの人に依頼されて作ります。 つまり, 自分の興味, 自分の得意分野などはあまり関係なく他人の都合で作ることが多いわけです。

たとえば, 銀行のオンラインシステムを動かすプログラムを作るのならば, 利用者がどのような操作(たとえば, 入金, 出金, 振込, 残高照会など)をするのかを把握して, 内部でどのような操作をする必要があるのかを細かく考えて, 間違いなくそうした操作を行うプログラムを作る必要があります。 利用者が使いやすい「インタフェース」にする必要がありますし, 銀行内部の処理に精通して, どのような操作がどのような状態の変化を引き起こす必要があるかを, 細部に渡って把握し, それからシステム全体を設計して, 具体的にプログラムにする必要があるのです。 利用者の思考や好みを想像して, その立場に立ったインタフェースを設計する必要がある一方で, 金融分野に関して専門家並みの知識が要求されます。

また, ずっと金融関係のシステムを構築していればよいということはまれで, 何ヶ月か(あるいは何年か)金融関係のシステムを開発したら, 次は図書館の図書予約・検索システムを開発するといった具合に, いろいろな分野に関して, 仕事しなければならないのが普通です。 したがって, 多方面に興味を持てる人の方が, プログラミング関連の仕事を続けていくのには向いていると言えるでしょう。 「自分には関係ないから興味ないなあ」と思った人もいると思いますが教育ソフトを受注したつもりになって考えてみてください。

DHC-オンライン講座には翻訳に関する講座もありますが, 上にあげたような面で翻訳とプログラミングには類似した所があります。 翻訳も他の人が書いた文章を訳すので, 「他人の都合」に合わせる必要があります。 また, いろいろな分野, たとえコンピュータ関連と絞ったとしても, その中でかなり幅広い種類の翻訳をこなす必要があります。 したがって, いろいろな方面に興味を持って調査をしたりしないとよい翻訳はできません。 さらに, 読みやすくわかりやすい文章に仕上げなければ, よい翻訳はできません。 この際には読み手(利用者)の思考を想像して, 理解しやすい訳を常に心がける必要があるわけです。

「条件分岐」の復習

まずは, とても単純に図3-1のようなダイアログを出して, 答えを入力してもらうプログラムを作りましょう。

図5-1 決まった問題ひとつだけの計算練習プログラム

次がこのためのプログラムです(うまく動かない場合は下の注意をお読みください)。

プログラム5-1 example05-01.html 計算練習1

注意!

Internet Explorer(インターネットエクスプローラ, 略してIE)を使っている場合は, 「この Web サイトは, スクリプト化されたウィンドウを使用して情報を依頼しています。 この Web サイトを信頼している場合, ここをクリックして, スクリプト化されたウィンドウを許可してください。 」という警告が出ることがあります。

その場合は, 次の手順でIEの設定を変更してから実行してください。

  1. [ツール]メニューから[インターネットオプション]を選択します。
  2. 上に並んでいる「タブ」から[セキュリティ]というタブを選択(クリック)します。
  3. 上に表示されている「ゾーン」の設定が「インターネット」になっていることを確認してから, 下の方にある[レベルのカスタマイズ]のボタンを選択します。
  4. 新しいウィンドウ中にある[スクリプト化されたウィンドウを使って情報の入力を求めることを Web サイトに許可する]の下の選択肢のうち[有効にする]を選択します。

なお, この設定を変更しても, 問題が起こることは通常はありません。 心配な方は, この講座が終わったら設定を元に戻してください。

Lesson 4で学んだ, if ... else ...が使われていますが, それさえわかればあとは難しくありません。

このJavaScriptのプログラム(スクリプト)も, 22行目の<body>タグに"onload()=..."が指定されているので, このページが読み込まれたときに実行されます。

  22  <body onload="keisan()">

再度このプログラムを実行したい場合は「更新(再読込み)」をすることになります。

8行目で問題として表示する文字列を, 9行目で答えをそれぞれ変数に代入します。

   8      var mondai = "3+4 =";
   9      var kotae  = 7; 

前のLessonでは次のように2文に分けていたのを, ここでは合わせて1行に書いています。

    var mondai;
    mondai = "3+4 =";

変数の「宣言」とその「初期値」(一番最初に記憶される値)の代入を一度にやっているわけです。

10行目で, 問題をダイアログボックスを使って表示します。

     var x = prompt(mondai, "");

関数alert()は単にメッセージを表示するだけでしたが, ここでは利用者から答えを受け取らないといけないので, この機能を持つ関数prompt()を使っています(promptは「促す」などの意味を持つ単語です。 「入力を促進するもの」「入力のためのウィンドウ」という意味で使われます)。 ここでも, 変数xの宣言をするのと同時に, prompt()からの答え(戻り値)をxに代入しています。 この行も次のように書いても同じことになります。

    var x;
    x = prompt(mondai, "");

このふたつの例のように, 初期値が決まる変数は宣言とともに初期値も指定してしまう方が簡潔でしょう。 ただ, 人によっては, 宣言と初期値は必ず別の文にする人もいます。

メモ

関数にはprompt()のように, 値を戻す関数と, Lesson 3で作ったsayHello()のように値を戻さないものがあります。 下で見ますが, 自作の関数でも値を返すようにもできます。

11行目から16行目で, 答えがあっていれば「せいかいです。 」を, 間違っているときは「ざんねんでした。 」を表示します(小学校1年生が使うのだから知らない漢字は使わずに, ひらがなで出すことにします)。

    if (x != kotae) {
      alert ("ざんねんでした。 ");
    }
    else {
      alert("せいかいです。 ");
    }

11行目のif文の条件にある「!=」は「等しくない」ことを表します。 つまり, 変数xに入っている値が変数kotaeに入っている値と等しくない場合に次の文が実行されます。

      alert("ざんねんでした。 ");

メモ

このプログラムでは計算の答えを入力するのに(漢字と同じ扱いをされる)全角文字で数字を入力すると, たとえ正しい答えを入れても「ざんねんでした。 」となってしまいます。 本当は, 全角の入力の場合も「正解」とするのがよいのですが, 処理がかなり複雑になるので, ここでは半角の数字だけを入力するという前提で進めていきましょう。

このままのプログラムを実際に使ってもらおうとするならば, ダイアログボックスなどに説明を表示したり, 口で注意するなどをしないとまごつく人がいるかもしれません。 一番よいのは, 全角で入力してもよいことにすることですが, これは, もう少し実力がついてからトライしましょう。

正解になるまで繰り返し ── while文

さて, 答え合わせをするだけでなく, 正解が入力されるまで終わらないようにしてみましょう。 このような場合は, 繰り返し行う操作を表現する方法が必要になります。

プログラム5-2 example05-02.html 計算練習2

13行目までは, 前のプログラムとまったく同じですが, その下に次のような「while文」が入っています。

14  while (x != kotae) {
15    x = prompt("ざんねんでした。 もういちど。 \n" + mondai, "")
16  }

while文は次のような形式をもちます。

while (<条件>) {
  <処理部>
}

while文は<条件>が成立している限り<処理部>が繰り返し実行されます。 ループ(輪)のようにぐるぐる回ることからwhileループ(while loop)とも呼ばれます。 これ全体でひとつのwhile文ですが, 処理部にも文が書けます。 つまり, 文の中に文が入るという入れ子構造になることができます。

この例の場合, 「!=」は「等しくない」ことを表すので, xがkotaeと同じにならない限り{と}で囲まれた<処理部>を繰り返し行うことになります。 最初に14行目に到達するときには, 13行目で入力された値がxに入っています。 このxの値が7になっていれば, 15, 16行目は実行されず, 17行目に飛びます。

最初に14行目に到達したときに, xが7でなければ<処理部>を実行します。 15行目が実行されることになり, 今度は図3-2のようなダイアログボックスが表示されます。

prompt()の引数の「もういちど。 」の後ろにある「\n」は「改行」を表す特別な文字列で, このふたつの文字が連続して使われたときに限り改行の意味を表します。 「\」は環境によっては「¥」が使われます。 どのような環境でも「\」か「¥」のどちらかは使えるはずです。

図5-2 答えが間違っていたときに表示されるダイアログ

Prompt()の表示するダイアログボックスの入力欄に書かれた値は, 再度xに代入されます。

この例ではwhile文の<処理部>はこの1行だけからなっているので, 15行目の実行が終わると, 再度14行目に戻り「x != kotae」の条件を確認することになります。

xに7が入っていればこの条件は満たされるので17行目に飛ぶことになります。 7でない場合は再度15行目が実行され, さらに14行目に戻って判定が行われるというように, 繰り返されます。 このループを抜けられるのは, 正しい答え7を入力したときだけ, というわけです。

このようにある条件が満たされるまで繰り返し行う処理を記述するのに, while文が使えます。

下請けに出そう ── 値を返す関数

さて, さすがに同じ問題ばかりを出していたのではひどいので, 実行するたびに違う問題をやってもらうように変えてみましょう。 どのような問題が出てくるかわからない方が練習になります。

このような目的に最適なのが「乱数」です。 JavaScriptをはじめとするプログラミング言語には, ランダムな値をコンピュータの方で適当に出してくれる機構(関数)が備わっています。 これを使って, 「10以下の数字」+「10以下の数字」という計算問題を自動的に作って表示するようにしてみましょう(プログラム5-3)。

プログラム5-3 example05-03.html 計算練習3

このプログラムでは, 今までと同じような役目をするkeisan()という関数のほかに, random10()という関数も作りました。 このように, プログラムを作る際には, 必要なだけいくつでも関数を作ることができます。 大規模なプログラムになると, 何千, 何万もの関数を使うものもあります。

乱数の発生 ── Math.random()

random10()は, 1以上10以下の整数をに生成してくれます。 random10()は, 「下請け(外注先)」としてMath.random()という関数を呼びます。 Math.random()は0以上1未満の小数を生成してくれる, JavaScriptの処理系があらかじめ用意してくれている「組み込み(built-in)」の関数なのです。

まず, random10()の処理内容を見ましょう。 31行目で変数xを宣言すると同時に, ここにMath.random()が戻す値を入れます。

  31      var x = Math.random(); // 0 ≦ x < 1

つまり, この時点で, 0 ≦ x < 1という関係が成り立っています。

32行目は変数iの宣言です。

  32      var i;

iはInteger(整数)のiで「ここには整数の値が入りますよ」という意図を表しています。 たとえばransuとかrandomNumberといった名前にしてもかまいませんが, iとかj, kあたりの変数はこのように「ちょっとした整数を保存する変数」の役割をするためによく使われます。 ほかの人が作ったプログラムを読むときのためにも覚えておきましょう。

さて, その次はとても単純なif ... else if ... elseの構文です。

  34      if (x < 0.1) {
  35        i = 1;
  36      }
  37      else if (x < 0.2) {
  38        i = 2;
  39      }
     ...  ...
  58      else if (x < 0.9) {
  59        i = 9;
  60      }
  61      else {
  62        i = 10;
  63      }

内容はおわかりでしょう。 上から順に, xが0.1より小さい(0.1未満の)とき, 0.1以上0.2未満のとき, 0.2以上0.3未満の時といった「場合分け」をして1から10までのいずれかの値をiに代入しています。

最後の次の文はif文で決まったiの値をこの関数を呼んだものに返す(return)という意味になります。

  61      return(i);

returnの前で, iには1から10までの整数のいずれかが, しかも同じ確率で入っています。 なお, return(i)の括弧はなくてもかまいませんが, いくつかの言語では括弧が必須なので, いつも()をつける人もいます。 あってもなくても間違いではありません。 同じように「i+j」を「(i)+(j)」と書いても問題は起こりません(ただし, この場合は読みにくくなるかもしれないので, お勧めはできません)。

メモ

プログラミング言語によって, JavaScriptの「関数」に相当するものの呼び名が違います。 たとえば, Pascalというひと昔前まで大変広く使われていた言語では, 関数(function)という言葉で値を返すもの, 「手続き(procedure)」という言葉で値を返さないものを指します。 ウェブページの「裏側」でよく使われるPerlという言語ではどちらも「サブルーチン(subroutine)」という言葉で表します。 JavaScriptや, 今でも広く使われているC言語の場合は, すべてが関数と呼ばれます。

元請けの仕事は?

さて, 下請けに出した先 ── random10() ── からは1から10までの整数のいずれかが返ってきます。 下請けから返ってくる値を使って「元請け」側の関数keisan()で問題を生成します。

11-15行目は変数の宣言。 それぞれどのような目的に使われるかは, もっと下で見ましょう。

  11      var no1; // 第1項
  12      var no2; // 第2項
  13      var mondai;
  14      var seikai; 
  15      var kotae;

16行目には空行をおいて変数宣言の部分と実際の処理の部分とを見てすぐわかるように分けています。

17行目からが処理の始まりです。

  17      no1 = random10();
  18      no2 = random10();

この2行では, random10()を呼び出して, 1から10までのどれかの整数を受け取って, その値をno1あるいはno2という変数に代入します。 no1は足し算の第1項のつもりでno1としました。 no2は同様に第2項を表します(x+yという式で, xは第1項, yは第2項です)。 random10()が呼び出すMath.random()は1回目に呼ばれるときも2回目に呼ばれるときも, ランダムに0以上1未満の数(小数)を返すので, random10()も呼ばれるたびに1から10までの整数をランダムに返すことになります。

メモ

Lesson 4で触れたように, 関数名や変数は, aからzおよびAからZまでのアルファベットのどれかで始まっている必要がありますが, ふたつ目の文字からはアルファベットの他に数字を使うことができます。 たとえば, i486, mz3roadster, MA333, love2youなどの文字列は変数として使えます。 しかし, 4abcとか*345などは変数としては使えません。

このほか特別に「_」(アンダースコア)も使うことができ, この文字は先頭にも途中にも最後にも使えます(つまり, アルファベットの文字と同じように扱われます)。 実際上は, 単語を区切る役目で使われることが多いようです。 このほかJavaScriptの最近のバージョンでは「$」(ドル記号)も使えますが, これを使ってあえて前のバージョンで使えないようにする意味はないでしょう(将来「ライブラリ」を扱うようになると使うことになるかもしれません)。

19行目では答え合わせをするための正解(seikai)を計算して準備し, 20行目では画面に表示する問題(mondai)を作ります。

  19      seikai = no1 + no2;
  20      mondai = no1 + " + " + no2 + " =";

この2行では2種類の「+」が使われているので少し注意が必要です。 上(19行目)は単純で, 「数字の足し算」(普通の意味での足し算)をしています。 たとえば, 次を実行すると, seikaiには11が代入されます。

    no1 = 4;
    no2 = 7;
    seikai = no1 + no2;

これに対して下(20行目)の「+」は文字列としてつなげる(連結する)意味で使われています。 たとえば, 次を実行したとしましょう。

    x = "あれ" + "これ";

するとxには"あれこれ"という文字列が入ります。 そして, 次を実行すると, xには"リンゴを3個食べた。 "が代入されます。

    x = "リンゴを" + "3" + "個食べた。 ";

さらに, 次を実行しても, xには, "リンゴを3個食べた。 "が代入されます。

   i = 3;
   x = "リンゴを" + i + "個食べた。 ";

ちょっと注意が必要なのは, 最後の例の場合iはもともとは数字なのだけれども, 前に文字列があると, 数字として「足す」ことはできなくなってしまうので, その場合は自動的に文字列として扱ってつなげてくれています。

警告!

ここで問題です。 次の場合は, xには何が代入されるでしょうか?

   i = 3;
   x = "3" + i;

確かめるには, たとえば次のようにして, 答えを表示してみればよいでしょう。

   i = 3;
   x = "3" + i;
   alert(x);

この結果は, 図5-3のように「6」ではなく"33"(文字として3がふたつ連続したもの)になります。 文字列としての"3"は数字として足すことは, できないのです。 ほとんどのプログラミング言語では文字列としての3と数字としての3はしっかり区別して考える必要があります。

Lesson 1でみたように, コンピュータの内部では, 文字は「コード」で記憶しているのに対して, 数字は数字そのもので記憶されています。 "3"(文字列の3)はコンピュータの内部では, 通常は数字の3には対応していません。 数字と文字の扱いはまったく異なっているのです。

図5-3 数字の文字列と整数を「+」した結果は文字列

「+」の働きをまとめると, 次のようになります。

「+」や「-」などいろいろな「演算」をしてくれる記号のことを「演算子(オペレータ, operator)」といい, 前後に現れて演算の対象となる数や変数などのことを「被演算子(オペランド, operand)」といいます。 上のまとめをもう少し格好をつけて言うと次のようになります。

では次に行きましょう。 18行目以降は, この前の例題(乱数を使わずに自分で問題を決めて作った例題)の処理と似ていますが, 少し違っています。

  21      kotae = prompt(mondai, "");
  22      while (kotae != null && kotae != seikai) {
  23        kotae = prompt("ざんねんでした。 もういちど。 \n" + mondai, "")
  24      }

まず, 21行目に使われているprompt()は, alert()とよく似ていてダイアログボックスを表示する関数ですが, alert()が「OK」のボタンを出すだけなのに対して, prompt()は図5-2のように「キャンセル」のボタンも表示します。 「OK」をクリックした場合はprompt()は1を値として返しますが, 「キャンセル」のときについてはすぐ下で見ましょう。

whileの条件部分にはふたつの条件がでつながっています。 後ろの方でkotae(答)がseikai(正解)と同じかどうかのチェックをしています。 前の方には, nullというのが初登場しています。 これは, prompt()でダイアログが表示されたときに, 「OK」ではなくて「キャンセル」の方を押した場合にprompt()から返される値です。

nullは「無効な」「無意味な」といった意味の英語で, プログラミングの世界では「ヌル」と読まれることが多く(英語の発音だと「ナル」の方が近いでしょうが)「値がないこと」「無効な値であること」あるいは「何も指していないこと」など, 「否定的な意味合いを持つ特別な値」を示すために使われます。 この例の場合は, nullでダイアログボックスで「キャンセル」が押されたことを表します。

22行目のwhileの条件に「kotae != null」を書かずに「kotae != seikai」だけを書いたとすると, キャンセルを押したときもwhileの「実行部」が実行されてしまい「ざんねんでした...」と表示されてしまいます。 キャンセルを押した人に対して「ざんねんでした...」と表示するのは変なので, このような処理が必要です。

25-27行目のif文は正解の時のメッセージ。 ここに来るのは, kotaeがseikaiと等しいか, 「キャンセル」を押してkotaeにnullが入っているかのいずれかの時です。

  25      if ( kotae != null ) {
  26        alert("せいかいです。 ");
  27      }

25行目ではダイアログで「キャンセル」を押したときに「せいかいです。 」を表示しないようチェックしています。 「キャンセル」の場合は, 何も表示せずに終わるだけになります。

メモ

先に説明したように, 演算子と被演算子の間には, (半角の)スペース(空白文字)をいくつ書いてもかまいません。 上の例題では, 次のように「+」などの演算子と被演算子の間にスペースをおいて書いていました。

    mondai = no1 + " + " + no2 + " =";

しかし, 次のようにスペースをおかずに書いてもmondaiに代入される値は同じになります。

    mondai=no1+" + "+no2+"=";

ただ, この例の場合, とくに「+」がいろいろな場所に使われているので, 例題のように書いておくと区切りがわかりやすくなります。 字下げ(indent)と同様, 空行やスペース(空白文字)をうまく使って, 見やすい(読みやすい)プログラムを書くのも大切なことです。

これで, 全体の説明が終わりました。 関数keisan()の処理はwhile文の仕組みがわかれば難しくないでしょう。 ranndom10()の方も単純な場合分けです。 こうして全体をふたつのとても単純な処理(関数)に分けてしまうとプログラムの見通しがとてもよくなります。 もう一度リスト5-3を見て全体を確認してください。

random10()の改良 ── Math.floor()

さて上のふたつの例でrandom10()という関数を作りました。 if ... else if ...の構文を使って書きましたが, じつはもっとずっと簡単に書けます。

function random10() {  
// 1以上10以下の整数を返す
    return( Math.floor(Math.random()*10) + 1 );
}

ここでは, Math.floor()という関数(メソッド)を使っています。 この関数は, 「引数として指定された数の小数点以下を切り捨てた値」 ── 言い換えると「引数を超えない最大の整数」を返してくれるものです。 Math.floor(5.1)は5, Math.floor(9.8)は9というわけです。

上のrandom10()のMath.floor()には, Math.random()*10が渡されています。 「*」は掛け算(乗算)を表す演算子です。 なぜか, 「×」とか「÷」という記号はタイプライターになかったので, 代わりに「*」と「/」が使われることになったのでしょう(タイプライターでも「*」と「/」を使って掛け算や割り算を表していたかもしれませんね)。

さて, Math.random()は0≦x<1を満たす値xを返すので, これを10倍すると, 0≦x<10を満たすような値xがMath.floor()に渡されることになります。 Math.floor()によって切り捨てした値に1を足すと, 1から10までの整数が返されるわけです。

たとえば, Math.random()から, 0.234が返ってきたとしてみましょう。 10倍するので2.34。 Math.floor(2.34)は.34を切り捨てて2になるので, 最終的には3が返ります。 前のif... else ifのバージョンのrandom10()では, 0.234は下に書いたelse ifに引っかかるので, random10から返る値は3と, Math.floor()を使ったバージョンと同じになります(どの場合も同じになることを確認してみてください)。

    else if (x < 0.3) {
        i = 3;
    }

というわけで, 前の例では30行以上も費やしていたrandam10()をコメントを入れてもたったの4行で書けてしまうことになりました。

メモ

このように, 組み込みの関数(メソッド)を使うと, プログラムがとても簡単になる場合があります。 プログラミングに慣れてきたら, マニュアルや参考書などを見て, どのような機能があらかじめ提供されているか検討をつけておくとよいでしょう。 ここで見た「乱数の生成」や「切り捨て」などは, ほとんどのプログラミング言語で提供されています。 一方, 特定のプログラミング言語が得意とする分野用に特殊な関数などを提供している場合もあります。

組み込みの関数などのことを(システムが提供する)「ライブラリ(library, 図書館, 蔵書などの意)」と呼ぶこともあります。 JavaScriptにも豊富なライブラリが用意されていて, さまざまなことができるようになっています。 ただ, そういったライブラリの内容に関する知識を得るのはなかなか時間がかかります。 プログラムの基本的なことに慣れてしまったあとは, ライブラリをいかに使いこなせるかが, その言語を使いこなすための重要なポイントになるのです。

関数は汎用に

最初の頃はひと桁の足し算だけをしていた小学1年生も, 夏休み前になると, 引き算や10を超える数の足し算も勉強するようになります。 また, もっと大きくなれば100を超えるような「被演算子(operand)」を使った計算をすることになります。 というわけで, 足し算だけでなく引き算の練習もできるように, そして, もっと大きな数も扱えるようにプログラムを拡張してみましょう。

引き算を追加する前に, ひとつ準備をしておきます。 random10()では, 1から10までの整数しか返ってこないので, これを特定の場合だけでなく, いろいろな場面で使えるように(「汎用」なものに)して, 引数に指定した整数を超えない整数を返すrandomInt()という関数に拡張します。 たとえば, 次のようにすると, xに1から20までの間の整数がランダムに返ってくるように変えようというわけです。

    x = randomInt(20);

じつは, これは簡単で, random10の「10」のところを引数の値にすればそれでおしまいです。

function randomInt(i) {
// 1以上i以下の整数を返す
    return( Math.floor(Math.random()*i) + 1 );
 }

先ほどのスクリプトのrandom10()のところを, randomInt()に変えて, 引数として10を渡せば, まったく同じ動作をするプログラムができあがります。 また, 引数として9を渡せば, 次のプログラムのように9までの整数を使った足し算練習ができあがります。

プログラム5-4 example05-04.html 計算練習4

12行目と13行目に注意してください。

  12      var no1, no2; // 第1項, 第2項
  13      var mondai, seikai, kotae; // 問題, 正解, 生徒の答え

これまでは変数を1行にひとつずつ宣言してきましたが, このようにまとめていくつか宣言することもできます。

ただし, この例のように役割が明白な変数についてはかまいませんが, 大きなプログラムをつくる場合は変数を宣言したらその隣にコメントを書いて説明することを強くお勧めします。 他人あるいは自分が後で読んだときに, どのような目的の変数かがすぐにわかるようにしておきます。

引き算も入れよう!

さて, randomInt()で任意の大きさの正数を生成できるようになったので, いよいよ引き算を出すよう拡張しましょう。 まずは, 10までの数字を使ってx+yあるいはx-yの形の問題を出します。 小学校1年生用の問題なのだから答えが負の数(マイナス)になってはいけないことに気を付ける必要があります。 「3-5=」などという問題を作ってはいけません。

このために, 関数keisan()の定義を次のようにします。

  11  function keisan() {
  12    var no1, no2; // 第1項, 第2項
  13    var mondai, seikai, kotae; // 問題, 正解, 生徒の答え
  14    var enzan;  // "+" か "-" 
  15  
  16    no1 = randomInt(10);
  17    if (randomInt(2) == 1) { //足し算の場合
  18      enzan = " + ";
  19      no2 = randomInt(10);
  20      seikai = no1 + no2;
  21    }
  22    else {  // 引き算の場合
  23      enzan = " - ";
  24      no2 = randomInt(no1);
  25      seikai = no1 - no2;
  26    }
  27    mondai = no1 + enzan + no2 + " =";
  28    kotae = prompt(mondai, "");
  29    while (kotae != null && kotae != seikai) {
  30      kotae = prompt("ざんねんでした。 もういちど。 \n" + mondai, "")
  31    }
  32    if ( kotae != null ) {
  33      alert("せいかいです。 ");
  34    }
  35  }

14行目で, enzan(演算)という変数を宣言します。

  14    var enzan;  // "+" か "-" 

これで足し算か引き算かを表します。 足し算か引き算かを決めるのは17行目の次のifです。

  17    if (randomInt(2) == 1) { //足し算の場合

randomInt(2)で, 1か2かいずれの値をランダムに生成して, 「1なら足し算をする, 2なら引き算をする」としています。

次のようにMath.random()をここで使っても, もちろんかまいません。

    if (Math.random()<0.5) {
      // 足し算の処理
    else {
      // 引き算の処理
    }

しかし, randomInt()を1度作っておくと, 「Math.random()の返す値には0や1は含むのだったかな」などと悩まずにすむし, keisan()の中ではrandomInt()だけを扱えばよくなるので, 話が単純になります。

メモ

大規模なプログラミングになると, 全体でかなり複雑な処理をすることになります。 複雑な処理をする場合ほど, 各部分をできるだけ単純に, 簡単にわかるようにしておかないと全体の把握が難しくなります。

プログラミングをするのに「できるだけ処理を単純にする」というのは非常に重要です。 バグ(エラー)のない(少ない)プログラムを作るための, 重要な心がけです。

16行目のno1の値の決め方はこれまでと同じ。 ただし, no2の値は, 足し算をするのならば,

  no2 = randomInt(10); 

でよいでしょうが, 引き算をするのならば, no1より大きい値にするわけにはいかないので, 次のようにしなければなりません。

    no2 = randomInt(no1); 

その処理を判定しているのが, 17-26行目のif ... else ...です。

  17      if (randomInt(2) == 1) { //足し算の場合
  18          enzan = " + ";
  19          no2 = randomInt(10);
  20          seikai = no1 + no2;
  21      }
  22      else {  // 引き算の場合
  23          enzan = " - ";
  24          no2 = randomInt(no1);
  25          seikai = no1 - no2;
  26      }

27行目でmondai(問題)を作るために, no1とenzanとno2, それに" ="を連結しています。

  27      mondai = no1 + enzan + no2 + " =";

ここから下は今までと同じで, 正解になるまで(あるいはキャンセルするまで)ダイアログを表示して入力を求めます。

これで, 1から10までの数字を使った足し算, 引き算を1回だけするプログラムが完成しました。

ドリル

さて, 次の問題をするのに「ページを再度読み込んでください」ではあまりにひどいので, 次のように変更(拡張)しましょう。

「繰り返し」問題を生成するのだからwhileが使えます。 次のようにしてみましょう。

  11  function keisan() {
  12    var no1, no2; // 第1項, 第2項
  13    var mondai, seikai, kotae; // 問題, 正解, 答え
  14    var enzan;  // "+" か "-" 
  15  
  16    while (true) {
  17      no1 = randomInt(10);
  18      if (randomInt(2) == 1) { //足し算の場合
  19        enzan = " + ";
  20        no2 = randomInt(10);
  21        seikai = no1 + no2;
  22      }
  23      else {  // 引き算の場合
  24        enzan = " - ";
  25        no2 = randomInt(no1);
  26        seikai = no1 - no2;
  27      }
  28      mondai = no1 + enzan + no2 + " =";
  29      kotae = prompt(mondai, "");
  30      while (kotae != null && kotae != seikai) {
  31        kotae = prompt("ざんねんでした。 もういちど。 \n" + mondai, "")
  32      }
  33      if ( kotae == null ) { // キャンセルボタンで終了
  34        break;
  35      }
  36    } // while (true) 
  37  }

今までのプログラムを13行目からのwhile (true) { ... } でくくっています。 つまり, 今まで1回しかやらなかったことを, 繰り返しやるわけです。 trueは「真」を意味する「値」(真偽値)で, whileの判定部分がいつも真なので, いつでも「処理部」を実行することになります。

いつまでもぐるぐる回っているわけにはいきません。 これを止めるのが, 31行目のbreakです。

  33        if ( kotae == null ) { // キャンセルボタンで終了
  34            break;
  35        }

breakはループを途中で抜けるときに使うもので, この場合, kotaeがnullに等しい場合, つまりダイアログボックスの「キャンセル」を押された場合にbreakが囲まれているループ(この場合whileループ)を抜けることになります。

36行目のコメント(プログラムには影響を与えない人間のためのメモ)

  36    } // while (true) 

は, whileループは16行目のwhile (true)から始まっているループ(外側のループ)がここで終わることを示しています。 この例ではwhileループの中に, if ... else ...や, 別のwhileループが使われています。 「}」がどの「{」に対応しているのか簡単にわかるように, こういったコメントをつけておくのも悪くない習慣です。

これで, 1から10までの数字を使った加減算を「キャンセル」をクリックするまで繰り返し練習するプログラムが完成しました。

まとめ

繰り返しの処理をするwhileループを紹介しました。 また, 値を返す関数の作り方を説明しました。

プログラミング一般

JavaScriptの構文

JavaScriptの関数(メソッド)

DHC-オンライン講座
文系の人にもわかる プログラミング入門
Lesson 5