はじめに
初めてのJavaScript(オライリー本)を調達しましたが、筆者にとって少しとっつきにくい。そこで、本書のサポートページにあるコラム『JavaScriptで学ぶ文系の人にもわかるプログラミング入門』から出発することにしました。
学習すること
変数、演算子、関数、分岐(if...else)、繰り返し(whie...,for...)など、多くの概念が登場しましたが、まだとても重要なものがひとつ残っています。ごくまれな例外を除いて、ほとんどすべてのプログラミング言語に備わっているのが「配列(array)」です。この章では、この「配列」を使って、「計算練習」をさらに発展させていきましょう。
目次
- ①同じ問題は出したくないー配列に記憶
- ②グローバル変数(大域変数)とローカル変数(局所変数)
- ③たくさんの値をまとめて記憶するー配列
- ④まとめ
①同じ問題は出したくないー配列に記憶
前の章の最後で、採点まで表示することになり、だいぶ計算ドリルらしくなりましたが、まだまだ改良の余地があります。まず気になるのが、1回のドリル(たとえば20問)の中で、同じ問題が出題されることです。前にまちがっていたので、もう1回同じのを出すのなら話は別ですが、簡単にできたのに同じのを出すのは止めたいところです。この問題を解決するには、それより前に出した問題を記憶しておく必要があります。そのために配列を使いましょう。
プログラム7-1 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>
②グローバル変数(大域変数)とローカル変数(局所変数)
var MondaiKioku = new Array();
var MondaiBangou = 0;
これまでの変数はすべてfunction xxxx {...}の...の範囲に入っていたのに、
この2行はどの関数にも属していません。このように変数を宣言すると、「大域変数(グローバル変数、global variable)」を宣言したことになります。今まで使ってきた変数は、関数の本体を表す{...}の...の部分に書かれており、この場合その関数内部でしかその変数にアクセスすること(その変数の値を見たり、その変数に値を代入したりすること)はできませんでした。関数内部でのみ有効な「局所変数(ローカル変数、local variable)」だったのです。
③たくさんの値をまとめて記憶するー配列
出した問題を次々に記憶していかなければならないので、そのためにかなりの数の変数が必要になります。そのための機構が「配列」です。
74行目は配列の宣言で、MondaiKiokuという配列を新たに作成しています。実際にこの配列を使っているのは78行目からのfor文の中です。
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個も出してないので、前に出した問題と比較する必要はないというわけです。
問題(文字列)をMondaiKioku[MondaiBangou] (配列)に代入
for文に続く次の行が実行されるとき、MondaiBangouは0になって、変数mondaiにこの上で生成した問題が文字列として入っています。
MondaiKioku[MondaiBangou] = mondai;
//「MondaiKioku[0] = mondai;」が実行されるのと同じ
// 問題1はMondaiKioku[0]、問題2はMondaiKioku[1]、問題3はMondaikioku[2]に格納
④まとめ
全体の動作
- グローバル変数の配列を使って、それまでに出した問題を記憶しておく。
- 問題を生成時に、配列に記憶されている要素すべてとの比較により、すでに出した問題かどうかをチェックする。このときループ(for文)を使う。
- 同じ問題だった場合は、もう一度生成して同じようにチェックする。これをまったく新しい問題が生成されるまで繰り返す。
プログラミング一般
- 配列(Array())
- グローバル変数(大域変数)とローカル変数(局所変数)
JavaScriptの構文
- do...while文ーー最低1回は処理部を実行するループ
do {
<処理部>
} while (<再実行の条件>);`
⑤次回やること
第7章までの「計算練習」では、ダイアログボックスを使って問題を1問ずつ表示しては答えを入力してもらって、ブラウザのウィンドウには簡単なメッセージを書いただけでした。それなりに計算の練習にはなるでしょうが、利用者の使い勝手(使いやすさ)を考えると大きな改良の余地がありそうです。
おわり
今回はここまでです。