💭

【初心者向け】for文とwhile文の使い分け完全ガイド

2025/03/13に公開
3

プログラミングにおいて、同じ処理を繰り返し実行する「ループ処理」は非常に重要な要素です。この記事では、主なループ文であるfor文とwhile文の違いや、それぞれの特徴、適切な使い分けについて詳しく解説します。

ループ処理とは?

ループ処理とは、特定の条件が満たされる間、同じ処理を繰り返し実行することです。例えば:

  • 配列の全要素に対して同じ処理を行う
  • ユーザーが正しい入力をするまで質問を繰り返す
  • ファイルの終わりまでデータを読み込む

このような処理を、一行ずつ書くのではなく、ループを使って効率的に実現します。

for文の基本

基本構文

for (初期化; 条件式; 更新式) {
    // 繰り返し実行する処理
}

各部分の役割:

  • 初期化: ループ開始前に1回だけ実行
  • 条件式: 各繰り返しの前にチェック(trueなら続行)
  • 更新式: 各繰り返しの最後に実行

具体例

// 1から5までの数を表示
for (let i = 1; i <= 5; i++) {
    console.log(i);
}
// 出力:
// 1
// 2
// 3
// 4
// 5

// 配列の要素を処理
let fruits = ["りんご", "バナナ", "オレンジ"];
for (let i = 0; i < fruits.length; i++) {
    console.log(`${i + 1}番目の果物: ${fruits[i]}`);
}
// 出力:
// 1番目の果物: りんご
// 2番目の果物: バナナ
// 3番目の果物: オレンジ

for...of文とfor...in文

配列やオブジェクトの処理に特化したfor文の変種もあります:

// for...of: 配列の要素を直接取得
let colors = ["赤", "青", "緑"];
for (let color of colors) {
    console.log(color);
}

// for...in: オブジェクトのプロパティを取得
let person = {
    name: "田中",
    age: 25,
    city: "東京"
};
for (let key in person) {
    console.log(`${key}: ${person[key]}`);
}

for await...of文

非同期処理を扱う際に便利なfor await...of文もあります。これは、Promiseの配列や非同期イテレータを順番に処理する場合に使用します:

// 非同期関数の例
async function fetchUserData(id) {
    const response = await fetch(`https://api.example.com/users/${id}`);
    return response.json();
}

// 複数のユーザーデータを順番に取得
async function getAllUsers() {
    const userIds = [1, 2, 3];
    const userPromises = userIds.map(id => fetchUserData(id));

    // for await...ofを使用して順番に処理
    for await (const userData of userPromises) {
        console.log('ユーザーデータ:', userData);
    }
}

// ファイルの非同期読み込みの例
async function* readFiles(files) {
    for (const file of files) {
        const content = await readFile(file, 'utf8');
        yield content;
    }
}

async function processFiles() {
    const files = ['file1.txt', 'file2.txt', 'file3.txt'];
    
    // for await...ofを使用してファイルを順番に読み込み
    for await (const content of readFiles(files)) {
        console.log('ファイル内容:', content);
    }
}

for await...ofは以下のような場合に特に有用です:

  1. APIからの連続データ取得

    • 複数のリクエストを順番に処理
    • レート制限を考慮した処理
  2. ファイル操作

    • 大きなファイルの逐次読み込み
    • 複数ファイルの順次処理
  3. ストリームデータの処理

    • WebSocketからのデータ受信
    • センサーデータの継続的な読み取り

while文の基本

基本構文

while (条件式) {
    // 条件式がtrueの間、繰り返し実行する処理
}

具体例

// カウントダウン
let count = 5;
while (count > 0) {
    console.log(count);
    count--;
}

// ユーザー入力の検証
let input;
while (input !== "quit") {
    input = prompt("コマンドを入力してください(終了するにはquitと入力):");
    console.log(`入力されたコマンド: ${input}`);
}

do...while文

条件をチェックする前に、必ず1回は処理を実行したい場合に使用します:

let answer;
do {
    answer = prompt("1 + 1 = ?");
} while (answer !== "2");

for文とwhile文の使い分け

for文が適している場合

  1. 繰り返し回数が明確な場合
// 配列の処理
let numbers = [1, 2, 3, 4, 5];
for (let i = 0; i < numbers.length; i++) {
    console.log(numbers[i] * 2);
}

// 特定回数の繰り返し
for (let i = 0; i < 3; i++) {
    console.log("Hello!");
}
  1. カウンター変数を使用する場合
// 1から10までの合計を計算
let sum = 0;
for (let i = 1; i <= 10; i++) {
    sum += i;
}
  1. 配列やコレクションの処理
let students = ["田中", "鈴木", "佐藤"];
for (let i = 0; i < students.length; i++) {
    console.log(`${i + 1}番: ${students[i]}`);
}

while文が適している場合

  1. 終了条件が不明確な場合
// ユーザーが「終了」と入力するまで続ける
let input = "";
while (input !== "終了") {
    input = prompt("コマンドを入力してください(終了するには「終了」と入力):");
    // 入力された内容を処理
}
  1. 条件が満たされるまで続ける場合
// ランダムな数が10以上になるまで生成
let number = 0;
while (number < 10) {
    number = Math.random() * 20;
    console.log(`生成された数: ${number}`);
}
  1. ファイル読み込みなどの外部リソース処理
let data;
while ((data = readNextLine()) !== null) {
    // データを処理
}

実践的な例

1. 素数判定プログラム

function isPrime(n) {
    if (n <= 1) return false;
    if (n <= 3) return true;
    
    // for文: 繰り返し回数が明確
    for (let i = 2; i <= Math.sqrt(n); i++) {
        if (n % i === 0) return false;
    }
    return true;
}

// while文: ユーザーが終了するまで続ける
let input;
while (input !== "quit") {
    input = prompt("数字を入力してください(終了はquit):");
    if (input === "quit") break;
    
    const number = parseInt(input);
    if (isNaN(number)) {
        console.log("有効な数字を入力してください");
        continue;
    }
    
    console.log(`${number}${isPrime(number) ? "素数です" : "素数ではありません"}`);
}

2. 配列の検索と処理

// 商品データ
const products = [
    { id: 1, name: "りんご", price: 100 },
    { id: 2, name: "バナナ", price: 200 },
    { id: 3, name: "オレンジ", price: 150 }
];

// for文: 配列の全要素を処理
function displayProducts() {
    console.log("=== 商品一覧 ===");
    for (let i = 0; i < products.length; i++) {
        console.log(`${products[i].name}: ${products[i].price}`);
    }
}

// while文: 特定の条件を満たすまで検索
function findProduct(searchName) {
    let i = 0;
    while (i < products.length) {
        if (products[i].name === searchName) {
            return products[i];
        }
        i++;
    }
    return null;
}

3. データ入力と検証

class UserInput {
    static getValidAge() {
        while (true) {
            const input = prompt("年齢を入力してください(18-120):");
            const age = parseInt(input);
            
            if (!isNaN(age) && age >= 18 && age <= 120) {
                return age;
            }
            console.log("無効な年齢です。18から120の間で入力してください。");
        }
    }
    
    static getValidEmail() {
        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        let email;
        
        do {
            email = prompt("メールアドレスを入力してください:");
        } while (!emailRegex.test(email));
        
        return email;
    }
}

4. ゲームのメインループ

class SimpleGame {
    constructor() {
        this.score = 0;
        this.isGameOver = false;
    }
    
    run() {
        // while文: ゲームが終了するまで続ける
        while (!this.isGameOver) {
            this.update();
            this.render();
            
            if (this.score >= 100) {
                this.isGameOver = true;
            }
        }
        
        console.log(`ゲーム終了!最終スコア: ${this.score}`);
    }
    
    update() {
        // for文: 固定回数の更新処理
        for (let i = 0; i < 5; i++) {
            this.score += Math.floor(Math.random() * 10);
        }
    }
    
    render() {
        console.log(`現在のスコア: ${this.score}`);
    }
}

よくある間違いと注意点

1. 無限ループ

// 悪い例: 終了条件が更新されない
while (true) {
    console.log("無限ループ!");
}

// 良い例: 適切な終了条件
let count = 0;
while (count < 5) {
    console.log(count);
    count++;  // カウンターを更新
}

2. 配列の境界チェック

// 悪い例: 配列の範囲外にアクセス
let arr = [1, 2, 3];
for (let i = 0; i <= arr.length; i++) {  // <= を使用している
    console.log(arr[i]);  // 最後の要素はundefined
}

// 良い例
for (let i = 0; i < arr.length; i++) {  // < を使用
    console.log(arr[i]);
}

3. ループ変数のスコープ

// 悪い例: varを使用
for (var i = 0; i < 3; i++) {
    // iはループ外でも参照可能
}
console.log(i);  // 3が出力される

// 良い例: letを使用
for (let i = 0; i < 3; i++) {
    // iはループ内でのみ有効
}
// console.log(i);  // エラー: iは定義されていない

パフォーマンスとベストプラクティス

1. 配列の長さを変数に格納

// 悪い例: 毎回lengthを計算
for (let i = 0; i < array.length; i++) {
    // 処理
}

// 良い例: lengthを事前に計算
const len = array.length;
for (let i = 0; i < len; i++) {
    // 処理
}

2. break文とcontinue文の適切な使用

// break文の例: 特定の条件で早期終了
for (let i = 0; i < items.length; i++) {
    if (items[i].isTarget) {
        console.log("対象が見つかりました");
        break;  // ループを終了
    }
}

// continue文の例: 特定の条件をスキップ
for (let i = 0; i < numbers.length; i++) {
    if (numbers[i] < 0) {
        continue;  // 負の数はスキップ
    }
    console.log(numbers[i]);
}

3. 適切なループの選択

// 配列の処理: for...ofを使用
const numbers = [1, 2, 3, 4, 5];
for (const num of numbers) {
    console.log(num);
}

// オブジェクトの処理: for...inを使用
const person = { name: "山田", age: 30 };
for (const key in person) {
    console.log(`${key}: ${person[key]}`);
}

// 条件付きループ: whileを使用
let attempts = 0;
while (attempts < 3) {
    // 処理
    attempts++;
}

まとめ

for文とwhile文の使い分けのポイントは以下の通りです:

for文を使う場合

  • 繰り返し回数が明確な場合
  • 配列やコレクションの処理
  • カウンター変数を使用する処理

while文を使う場合

  • 終了条件が不明確な場合
  • ユーザー入力の処理
  • 条件が満たされるまで続ける処理

どちらを選ぶかは、以下の点を考慮して判断しましょう:

  1. 処理の性質

    • 回数が決まっているか
    • 条件で終了するか
    • データ構造に依存するか
  2. 可読性

    • どちらがコードの意図を明確に表現できるか
    • メンテナンスのしやすさ
  3. パフォーマンス

    • 処理の効率性
    • メモリの使用

適切なループ文を選択することで、より効率的で読みやすいコードを書くことができます。

次のステップ

ループ処理の基本を理解したら、以下の記事も参考にしてください:

Discussion

junerjuner
宝松@システムエンジニア宝松@システムエンジニア

おっ、良い指摘ですね!🎯 for await ... of は非同期処理で便利ですよね!Promise が含まれるデータの逐次処理にはもってこいです💡

たとえば、API から複数のデータを順番に取得するときに for await ... of を使うと、シンプルに書けますね👇

async function fetchData(urls) {
  for await (const url of urls.map(fetch)) {
    const data = await url.json();
    console.log(data);
  }
}

非同期処理をうまく使いこなせると、コードの可読性もグッと上がりますよね!
また面白い使い方があれば、ぜひシェアしてください✨

junerjuner

setTimeout をPromise に変換して await するように
setInterval を asyncIterator に変換して for await ... of するとネストを浅く保てて良き
(勿論 setTimeout を 使って generator 書いて for await ... of するほうが簡単ではありますが