1.なぜ繰り返し文が必要なのか
第1講では、プログラムを順序・条件・繰り返しに分解してみました。第3講の条件文が「分かれ道」だとすれば、今回の繰り返し文は「引き返す道」です。1から5までを画面に出すなら console.log を5行書いてもかまいませんが、1から1,000までとなると話が変わります。行をコピーして貼り付けると間違えやすく、数字を変えたくなったらすべての行を直さなければなりません。
繰り返し文は「この作業を、この条件が成り立つ間ずっと続けなさい」と一度だけ書く文法です。人は同じ作業を長く続けると疲れて間違えますが、コンピューターは百回目も一回目とまったく同じようにこなします。その代わり、コンピューターはいつ止まるべきかを自分では判断できないので、止める条件を人が正確に書いてあげる必要があります。この講義では終始、「何を繰り返すのか」と「いつ止めるのか」をセットで考える練習をします。
下のコードは、5行書く代わりに繰り返し文一つで同じ結果を出します。数字の 5 を 1000 に変えれば1,000行が出力されますが、コードは数文字しか変わりません。これが、繰り返し文が自動化の出発点である理由です。
for (let i = 1; i <= 5; i++) {
console.log(i + '回目のあいさつ: こんにちは');
}1回目のあいさつ: こんにちは 2回目のあいさつ: こんにちは 3回目のあいさつ: こんにちは 4回目のあいさつ: こんにちは 5回目のあいさつ: こんにちは
2.`for` 文 — 回数が決まっている繰り返し
for 文のかっこの中には、セミコロンで区切られた三つの部分が入ります。一つ目は最初に一度だけ実行する準備(let i = 1)、二つ目は毎回繰り返す前に確かめる条件(i <= 5)、三つ目は一周が終わるたびに行うこと(i++)です。i++ は i = i + 1 を短く書いたものです。条件が true の間は波かっこの中の本体が実行され、条件が false になった瞬間に繰り返しが終わります。
実行の順番を正確に知っておくことが大切です。準備 → 条件の確認 → 本体 → 増加 → 条件の確認 → 本体 → 増加 … の順に回り、条件が偽になると本体を飛ばして繰り返し文の下へ進みます。そのため、最後の i は条件を初めて破った値になります。下の表のように変数の値を一行ずつ書き出すことを「トレース(追跡)」と呼び、繰り返し文がおかしな動きをしたときに真っ先に試すことです。
繰り返しの変数名に i を使うのは昔からの慣習にすぎず、規則ではありません。意味がはっきりするなら month や page のように名前を付けてもかまいません。繰り返しの変数は値が変わるので、const ではなく let で宣言します(第2講)。
const dan = 2;
for (let i = 1; i <= 4; i++) {
console.log(dan + ' x ' + i + ' = ' + dan * i);
}2 x 1 = 2 2 x 2 = 4 2 x 3 = 6 2 x 4 = 8
| 周 | `i` の値 | 条件 `i <= 4` | したこと |
|---|---|---|---|
| 1 | 1 | 真 | 2 x 1 = 2 を出力、i が2になる |
| 2 | 2 | 真 | 2 x 2 = 4 を出力、i が3になる |
| 3 | 3 | 真 | 2 x 3 = 6 を出力、i が4になる |
| 4 | 4 | 真 | 2 x 4 = 8 を出力、i が5になる |
| - | 5 | 偽 | 本体を飛ばして繰り返し終了 |
3.`for...of` と二つの基本パターン — 累積と数え上げ
リストに入った値を一つずつ取り出して使うときは、for...of がいちばん読みやすい書き方です。for (const x of list) と書くと、list の値が先頭から順に x に入ります。何番目かを数える必要がないので、番号を取り違える失敗が減ります。リスト(配列)は第6講で詳しく扱います。ここでは、角かっこの中に値をカンマで並べたものだとだけ覚えておけば十分です。
繰り返し文で行う作業の多くは、二つのパターンにまとめられます。累積パターンは、繰り返しの前に let sum = 0 のような「まだ何もない値」を用意しておき、一周ごとに値を足して積み上げます。数え上げパターンは let count = 0 から始め、条件に合うときだけ1を足します。どちらのパターンも初期値が重要です。合計の初期値は0、積の初期値は1、文字をつなげていくときの初期値は空文字列 '' です。
累積用の変数を繰り返し文の中で宣言すると、毎周0として作り直されて合計が積み上がりません。必ず繰り返し文の外、上の方で宣言してください。反対に、結果を出力する console.log は、ふつう繰り返し文が終わった下に置きます。中に置くと途中の合計が周ごとに出力されます。途中の値を見たいときは、わざと中に入れて追跡用に使うこともあります。
const sales = [12000, 8500, 30000, 4000];
let sum = 0;
let count = 0;
for (const won of sales) {
sum += won;
if (won >= 10000) {
count++;
}
}
console.log('合計', sum);
console.log('1万ウォン以上', count + '件');合計 54500 1万ウォン以上 2件
[70, 85, 90, 60] の平均を繰り返し文で求めたいとします。どんな順番で考えればよいでしょうか。const scores = [70, 85, 90, 60];
let total = 0;
for (const s of scores) {
total += s;
}
console.log(total / scores.length);76.25
- ステップ1 何を積み上げるか: 点数の合計を積み上げます。初期値は
0です。 - ステップ2 一周でやること: 点数を一つ取り出して合計に足します。70 → 155 → 245 → 305。
- ステップ3 繰り返しが終わったあと: 合計305を個数4で割ります。個数は
scores.lengthで得られます。 - 検算: 305 ÷ 4 = 76.25。70と90の間にあるので妥当です。空のリストだと 0 ÷ 0 になって
NaNが出るので、別に扱う必要があります。
4.`while` 文と無限ループ — 終了条件を変えるのは誰か
while 文は条件を一つだけ受け取り、条件が真の間は本体を実行し続けます。何回回るか前もってわからず、「目標に届くまで」繰り返したいときによく合います。たとえば毎月同じ額を積み立てて目標額に届くまで何か月かかるかは、計算式でも求められますが、一か月ずつ足しながら数えるのがいちばん直感的です。
while を使うときに必ず確かめたいのは、「本体の中に条件を変える行があるか」という問いです。下のコードで saved += 50000 がなければ saved は永遠に0のままで、条件も永遠に真です。これが無限ループです。無限ループに陥ったプログラムはエラーメッセージも出さずに回り続けるので、ブラウザーのタブが応答しなくなったように見えます。このページの練習場は、コードが2秒以内に終わらなければ強制的に止めて「時間制限」を知らせます。
無限ループを防ぐ点検方法は三つあります。条件に使われている変数が本体の中で変わるか、変わる向きが条件を偽にする方向か(0から始めて引くだけなら目標から遠ざかります)、そしてぴったり同じになったときだけ止まる !== の条件ではなく、超えても止まる < のような条件を使っているかです。第2講で見た 0.1 + 0.2 のように小数はぴったり同じにならないことがあるので、!== で止めるのは危険です。
const goal = 300000;
let saved = 0;
let months = 0;
while (saved < goal) {
saved += 50000;
months++;
}
console.log(months + 'か月後', saved + 'ウォン');6か月後 300000ウォン
console.log を使い、繰り返しの変数と累積用の変数を出力してみてください。何周目でどんな値になるかが見えれば、原因はほぼ明らかになります。周回が多すぎるときは、最初の数周だけを見られるように条件を一時的に小さくします。5.`break` と `continue` — 早めに止める、飛ばす
break は繰り返し文からすぐに抜け出します。リストの中で条件に合う最初の値だけ見つければよいとき、見つけたあとも最後まで回るのは無駄なので break で止めます。continue は今回の周の残りを飛ばして次の周へ進みます。空の値や不正な値を取り除き、残りだけを処理したいときに使います。
この二つのキーワードは便利ですが、使いすぎると流れを追いにくくなります。一つの繰り返し文に break と continue がいくつも混ざっているなら、条件文で書き直すか、第5講で学ぶ関数に分けたほうが読みやすくなります。特に関数の中では、return が繰り返し文と関数をまとめて終わらせるので、「見つけたらすぐ返す」という意図をよりはっきり示せます。
「見つからなかった場合」も必ず考えておきましょう。下の最初のコードで found の初期値を -1 にしているのは、「まだ見つかっていない」という印です。繰り返しが終わってもまだ -1 なら、リストに条件に合う値がなかったという意味です。配列の indexOf が見つからないときに -1 を返すのも同じ約束です(第6講)。
const temps = [3, 1, -2, 5, -4];
let found = -1;
for (let i = 0; i < temps.length; i++) {
if (temps[i] < 0) {
found = i;
break;
}
}
console.log('最初の氷点下の位置', found);最初の氷点下の位置 2
const items = ['りんご', '', 'なし', '', 'みかん'];
for (const name of items) {
if (name === '') {
continue;
}
console.log(name);
}りんご なし みかん
6.off-by-one — 一つずれて間違える、いちばんよくある失敗
繰り返し文のバグでいちばん多いのは、一回多く回ったり一回少なく回ったりする失敗です。英語では off-by-one(一つずれ)エラーと呼びます。原因はたいてい三つです。0から始めるか1から始めるか、条件に < を使うか <= を使うか、そして「個数」と「最後の番号」を取り違えることです。配列の位置は0から数えるので、長さが5の配列の最後の位置は5ではなく4です。
植木算の問題で感覚をつかみましょう。10mの柵に1m間隔で柱を立てると、柱は10本ではなく11本です。区間の数と端点の数は一つ違います。同じように、for (let i = 0; i < 5; i++) は0・1・2・3・4で5回、for (let i = 1; i <= 5; i++) は1〜5で5回、for (let i = 0; i <= 5; i++) は0〜5で6回回ります。同じ「5」が入っていても回数が違うのです。
off-by-one を捕まえるいちばん確実な方法は、境界値で実際に試すことです。第3講で条件文の境界値を確かめたように、繰り返し文では「0個のとき、1個のとき、最後の一つ」を必ず入れてみます。配列を位置で回していて i <= arr.length と一つはみ出すと、最後の周で arr[i] が undefined になり、合計が NaN に変わります。エラーメッセージなしにおかしな値が出るので、なおさら見つけにくくなります。位置が必要ないなら for...of を使うのが、この失敗をそもそも避ける方法です。
<= と < の一字違い — はみ出したマスは undefined、合計は NaNconst nums = [10, 20, 30];
let a = 0;
for (let i = 0; i <= nums.length; i++) {
a += nums[i];
}
console.log('一つはみ出した合計', a);
let b = 0;
for (let i = 0; i < nums.length; i++) {
b += nums[i];
}
console.log('正しい合計', b);一つはみ出した合計 NaN 正しい合計 60
- リストが空のとき(0個)に結果が筋の通ったものになるか — 合計0、個数0、平均は別に扱う
- 値が一つだけのとき、繰り返しがちょうど一回回るか
- 最後の値が処理されるか — 条件が
<か<=かを確認する - 条件に使われた変数が本体の中で変わり、いつか偽になるか
- 累積用の変数が繰り返し文の外で宣言されているか
7.繰り返し文を読んで判断する — AIが書いたコードを前にして
AIのコーディングアシスタントに「リストの合計を求めて」と頼めば、繰り返し文の入ったコードがすぐに手に入ります。けれども、そのコードが自分のデータで正しく動くかどうかは自分で確かめなければなりません。繰り返し文を読むときに探すのは三つだけです。繰り返しの前に何を用意するか(初期値)、一周で何をするか(本体)、いつ止まるか(終了条件)です。この三つをそれぞれ一文で言葉にできれば、そのコードを理解したことになります。
次に、小さな入力で手作業のトレースをしてみます。値が三つほどのリストを入れ、上で作ったトレース表のように周ごとの変数の値を書き出してから、実際の実行結果と比べます。空のリストと値が一つだけのリストも入れてみましょう。ここで結果が違ったら、AIに「この入力でこの結果が出たが、期待値はこれだ」と具体的に伝えると、ずっと早く直せます。エラーを読んで原因を絞り込む方法は第8講、AIのコードを検証する方法は第10講でさらに詳しく扱います。
📌 要点まとめ
- 繰り返し文は「何を繰り返すか」と「いつ止めるか」をセットで書く文法である
- 回数が決まっていれば
for、リストの値を一つずつ使うならfor...of、目標に届くまでならwhile - 累積は繰り返し前に初期値(合計なら0)を置いて足し上げ、数え上げは条件に合うときだけ1を足す
whileは本体に条件を変える行がなければ無限ループになる<と<=、0始まりと1始まり — 空のリスト・値一つ・最後の値で境界を試す
🧪 コード実習
繰り返し文で合計・個数・位置を求める関数を作ります。空のリストや0のような境界値のテストも一緒に採点されるので、実行する前にその場合をまず考えてみてください。
コードはこのブラウザの中の隔離された場所でだけ実行され、サーバーには送られません。ネットワークには接続できず、2秒を超えると止まります。書き換えたコードはこのブラウザにだけ保存されます。Ctrl+Enter(⌘+Enter)で実行、Tabキーは空白2つ(Escの後のTabで次へ移動)。
JavaScriptが無効なので実行はできませんが、問題・最初のコード・自動チェックの内容・解答例はそのまま読めます。
11からnまで足す
数 n を受け取り、1から n までの合計を返す関数 sumTo(n) を繰り返し文で作ってください。n が0なら0を返します。例: sumTo(5) は15。
sumTo(5)期待値15sumTo(1)期待値1sumTo(0)期待値0sumTo(100)期待値5050
💡 ヒント
条件を i < n と書くと最後の n が抜けます。n まで含めるには何を使えばよいでしょうか。
解答例を見る
function sumTo(n) {
let sum = 0;
for (let i = 1; i <= n; i++) {
sum += i;
}
return sum;
}2基準以上の点数を数える
点数の配列 scores と基準点 cut を受け取り、基準以上の点数がいくつあるかを返す関数 countAtLeast(scores, cut) を作ってください。空の配列なら0です。
countAtLeast([50,70,90], 70)期待値2countAtLeast([], 60)期待値0countAtLeast([60,60,59], 60)期待値2countAtLeast([10,20], 100)期待値0countAtLeast([0,0], 0)期待値2
💡 ヒント
「以上」は同じ値も含みます。> と >= の違いを確かめましょう。
解答例を見る
function countAtLeast(scores, cut) {
let count = 0;
for (const s of scores) {
if (s >= cut) {
count++;
}
}
return count;
}3最初の負の数の位置を探す
数の配列 nums で最初に出てくる負の数の位置(0から数える)を返す関数 firstNegative(nums) を作ってください。負の数がないか配列が空なら -1 を返します。見つけたらそれ以上回らずに止めてください。
firstNegative([3,1,-2,5,-4])期待値2firstNegative([-1,2])期待値0firstNegative([1,2,3])期待値-1firstNegative([])期待値-1firstNegative([0,4,-7])期待値2
💡 ヒント
関数の中では return が繰り返しと関数をまとめて終わらせます。「見つからない」ときの return -1 は繰り返し文が終わったあとに置きます。
解答例を見る
function firstNegative(nums) {
for (let i = 0; i < nums.length; i++) {
if (nums[i] < 0) {
return i;
}
}
return -1;
}4各桁の和(条件で回す繰り返し)
0以上の整数 n を受け取り、各桁の数字の和を返す関数 digitSum(n) を while で作ってください。例: digitSum(1234) は 1+2+3+4 = 10。n % 10 は最後の桁、Math.floor(n / 10) は最後の桁を取り除いた数です。
digitSum(1234)期待値10digitSum(0)期待値0digitSum(9)期待値9digitSum(1000)期待値1digitSum(99999)期待値45
💡 ヒント
n を減らす行を書き忘れると無限ループになります。条件に使った変数が本体の中で変わるか確かめましょう。
解答例を見る
function digitSum(n) {
let sum = 0;
while (n > 0) {
sum += n % 10;
n = Math.floor(n / 10);
}
return sum;
}🤖 AIにはこう聞いてみましょう
コピーして [ ] の部分を自分の状況に書き換えて使います。答えはそのまま信じず、本文と比べて確かめましょう。
繰り返し文がどう回るのか理解できないとき
下のJavaScriptの繰り返し文を入力 [小さな例の入力] で実行するとして、周ごとに変数の値がどう変わるかをトレース表(周・変数・条件の真偽・したこと)で見せてください。繰り返しが終わる瞬間の条件も指摘してください。コード: [貼り付け]
AIがくれた繰り返し文のコードを検証したいとき
次の繰り返し文のコードについて、初期値・本体・終了条件をそれぞれ一文で説明してください。そのうえで、空のリスト、値が一つ、最後の値、無限ループの可能性という観点から、境界テストの入力と期待結果を表にしてください。自分で実行して比べます。コード: [貼り付け]
繰り返し文の練習問題がもっと欲しいとき
JavaScriptの繰り返し文(`for`、`while`、`for...of`、`break`、`continue`)の練習問題を、易しいものから順に5問作ってください。累積・数え上げのパターンと off-by-one の落とし穴を必ず一つずつ入れ、データは架空の例にしてください。答えは伏せておき、私がコードを送ったら境界値の入力で採点してください。
🧰 関連ツール
この講義で扱ったJSON・正規表現・テキストを、その場で確かめたり整えたりできるツールです。本物の個人情報やパスワードは貼り付けないでください。
- MDN Web Docs — JavaScriptガイド: ループと反復処理(Loops and iteration)
- MDN Web Docs — for、while、for...of、break、continue 文のリファレンス
- ECMAScript言語仕様(ECMA-262)の繰り返し文の定義に関する一般的な内容
学習目標をすべて達成できたら完了を押しましょう。
このブラウザでは保存できないため、この画面でのみ表示されます。