Help us understand the problem. What is going on with this article?

【初心者向け】JavaScript入門⑤「配列(array)とグローバル変数」

はじめに

初めてのJavaScript(オライリー本)を調達しましたが、筆者にとって少しとっつきにくい。そこで、本書のサポートページにあるコラム『JavaScriptで学ぶ文系の人にもわかるプログラミング入門』から出発することにしました。

学習すること

第7章 配列とグローバル変数(loop)

変数、演算子、関数、分岐(if...else)、繰り返し(whie...,for...)など、多くの概念が登場しましたが、まだとても重要なものがひとつ残っています。ごくまれな例外を除いて、ほとんどすべてのプログラミング言語に備わっているのが「配列(array)」です。この章では、この「配列」を使って、「計算練習」をさらに発展させていきましょう。

目次
* ①同じ問題は出したくないー配列に記憶
* ②グローバル変数(大域変数)とローカル変数(局所変数)
* ③たくさんの値をまとめて記憶するー配列
* ④まとめ

①同じ問題は出したくないー配列に記憶

前の章の最後で、採点まで表示することになり、だいぶ計算ドリルらしくなりましたが、まだまだ改良の余地があります。まず気になるのが、1回のドリル(たとえば20問)の中で、同じ問題が出題されることです。前にまちがっていたので、もう1回同じのを出すのなら話は別ですが、簡単にできたのに同じのを出すのは止めたいところです。この問題を解決するには、それより前に出した問題を記憶しておく必要があります。そのために配列を使いましょう。

プログラム7-1 example07-01.html 計算問題

example07-01.html計算問題
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>けいさん8</title>
<!-- 配列を使って記憶して、同じ問題を出さない -->
<script type="text/javascript">
function keisan() {
    var mondaiSu; // 問題数
    var i;
    var seikaiNoKazu = 0;   // 正解
    var machigaiNoKazu = 0; // 間違い

    mondaiSu = prompt("たしざん ひきざん。" +
                      "なんかい やりますか?",20); // 問題数を入力させる
    for (i=1; i<= mondaiSu; i++) {         // 指定した分の問題数を表示する
        var kekka = tashizanHikizan(i);
        if (kekka == null) {           // キャンセルボタンを押されたとき(戻り値=null)
            break;
        }
        else if (kekka == 1) {          // 正解したとき(戻り値=1)
            seikaiNoKazu++;
        }
        else {                          // 間違えたとき(戻り値=0)
            machigaiNoKazu++;
        }
    }
    alert("せいかいのかず " + seikaiNoKazu + " もん:" +    //ダイアログボックス(回答結果)を表示
          "まちがいのかず " + machigaiNoKazu + " もん");
}

function tashizanHikizan(i) { // 足し算か引き算を1回実行
    var no1, no2; // 第1項、第2項
    var mondai, seikai, kotae; // 問題,正解,答え
    var enzan;   // "+"か"-"
    var kekka;   // 正解(1)か不正解か(0)
    var saidaiKotae = 19;

    do {                                 // なぜdo...while文なのか?
        no1 = randomInt(saidaiKotae-1);  // 最大答え(-1)の乱数をno1(第1項)に代入
        if(randomInt(2) == 1) {       // 足し算の場合
            enzan = " + ";
            no2 = randomInt(saidaiKotae-no1);
            seikai = no1 + no2;
        }
        else {                             // 引き算の場合
            enzan = " - ";               
            if (no1 > 10) {                // 第1項(no1)が10より大きい場合
                no2 = randomInt(no1-10);   // 第1項(no1)から10引いた数値までの乱数
            }
            else {                         // 第1項(no1)が10以下の場合
                no2 = randomInt(no1);     // 第1項の乱数を第二項に代入
            }
            seikai = no1 -no2;         // 計算結果をseikaiに代入
        }
        mondai = no1 + enzan + no2 + " ="; // 問題を生成(第1項 +/- 第2項 =)
    }
    while (mondaiChofuku(mondai));          // すでに前に出された問題であれば、処理を繰り返す(別の問題を生成する)

    kotae = prompt("だい" + i + "もん: " + mondai, ""); // 正解をkotaeに代入?
    kekka = 1;                         // 初期値(=正解)をkekkaに代入
    while (kotae != null && kotae != seikai) {          // キャンセルでも、正解でもない
        kekka = 0;                      // つまり、不正解
        kotae = prompt("ざんねんでした。もういちど:" + mondai, "") // 問題を再表示
    }
    if ( kotae == null ) {     // キャンセルボタンを押されたとき
        return null;              // 戻り値=nullを返す
    }
    else {                        // 正解のとき
        return kekka;             // 戻り値=1を返す
    }
}

// 「大域変数(グローバル変数,global variable)」※以前出された問題を記憶し続ける
var MondaiKioku = new Array();   // 配列をMondaiKiokuに代入
var MondaiBangou = 0;             // MondaiBangouの初期化(MondaiBangou=0だとforループが初回で終わらないか?)

function mondaiChofuku(mondai) {
    for(var i=0; i<MondaiBangou; i++) { // iがMondaiBangouより小さい間は繰り返す。「i(0) < mondaiBangou(0)」初回で終了?
        if (MondaiKioku[i] == mondai) { // 過去に同じ問題が出力されていないか?
            return true;          // 出題されてる?
        }
    }
    MondaiKioku[MondaiBangou] = mondai; // 
    MondaiBangou++;
    return false;
}

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

<body onload="keisan();">
<h1>けいさんれんしゅう</h1>
</body>
</html>

②グローバル変数(大域変数)とローカル変数(局所変数)

html;example07-01.html
var MondaiKioku = new Array();
var MondaiBangou = 0;

これまでの変数はすべてfunction xxxx {...}の...の範囲に入っていたのに、
この2行はどの関数にも属していません。このように変数を宣言すると、「大域変数(グローバル変数、global variable)」を宣言したことになります。今まで使ってきた変数は、関数の本体を表す{...}の...の部分に書かれており、この場合その関数内部でしかその変数にアクセスすること(その変数の値を見たり、その変数に値を代入したりすること)はできませんでした。関数内部でのみ有効な「局所変数(ローカル変数、local variable)」だったのです。

③たくさんの値をまとめて記憶するー配列

出した問題を次々に記憶していかなければならないので、そのためにかなりの数の変数が必要になります。そのための機構が「配列」です。
74行目は配列の宣言で、MondaiKiokuという配列を新たに作成しています。実際にこの配列を使っているのは78行目からのfor文の中です。

example07-01.html計算問題(74-86行目)
var MondaiKioku = new Array();
var MondaiBangou = 0;  // これまでにいくつ問題を生成したか?

function mondaiChofuku(mondai) {
    for (var i=0; i<MondaiBangou; i++) {
       if (MondaiKioku[i] == mondai) {  //問題を比較
      return true; //生成した問題が過去のものと重複してる
       }
    }
      MondaiKioku[MondaiBangou] = mondai;
      MondaiBangou++;  // 新たな問題を生成するたびに1プラス
      return false; //生成された問題はすでに出された問題ではない、の意。
}

for文の処理

for分の最初でまず変数iを宣言して0に初期化し、iがMondaibangouよりも小さいかチェックされます。一番最初にこの関数が呼ばれたときは、MondaiBangouの値は0で、iも0なのでこの条件は成り立たず(真にならず)、forの<処理部>は一度も実行されずに下に行ってしまいます。つまり、まだ問題は1個も出してないので、前に出した問題と比較する必要はないというわけです。

問題(文字列)をMondaiKiokuMondaiBangouに代入

for文に続く次の行が実行されるとき、MondaiBangouは0になって、変数mondaiにこの上で生成した問題が文字列として入っています。

example07-01.html計算問題(83行目)
MondaiKioku[MondaiBangou] = mondai;
//「MondaiKioku[0] = mondai;」が実行されるのと同じ
// 問題1はMondaiKioku[0]、問題2はMondaiKioku[1]、問題3はMondaikioku[2]に格納

④まとめ

全体の動作
* グローバル変数の配列を使って、それまでに出した問題を記憶しておく。
* 問題を生成時に、配列に記憶されている要素すべてとの比較により、すでに出した問題かどうかをチェックする。このときループ(for文)を使う。
* 同じ問題だった場合は、もう一度生成して同じようにチェックする。これをまったく新しい問題が生成されるまで繰り返す。

プログラミング一般

  • 配列(Array())
  • グローバル変数(大域変数)とローカル変数(局所変数)

JavaScriptの構文

  • do...while文ーー最低1回は処理部を実行するループ
     do {
          <処理部>
     } while (<再実行の条件>);`

⑤次回やること

第8章 HTMLの表機能

第7章までの「計算練習」では、ダイアログボックスを使って問題を1問ずつ表示しては答えを入力してもらって、ブラウザのウィンドウには簡単なメッセージを書いただけでした。それなりに計算の練習にはなるでしょうが、利用者の使い勝手(使いやすさ)を考えると大きな改良の余地がありそうです。

おわり

今回はここまでです。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away