💭
【初心者向け】for文とwhile文の使い分け完全ガイド
プログラミングにおいて、同じ処理を繰り返し実行する「ループ処理」は非常に重要な要素です。この記事では、主なループ文である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
は以下のような場合に特に有用です:
-
APIからの連続データ取得
- 複数のリクエストを順番に処理
- レート制限を考慮した処理
-
ファイル操作
- 大きなファイルの逐次読み込み
- 複数ファイルの順次処理
-
ストリームデータの処理
- 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文が適している場合
- 繰り返し回数が明確な場合
// 配列の処理
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から10までの合計を計算
let sum = 0;
for (let i = 1; i <= 10; i++) {
sum += i;
}
- 配列やコレクションの処理
let students = ["田中", "鈴木", "佐藤"];
for (let i = 0; i < students.length; i++) {
console.log(`${i + 1}番: ${students[i]}`);
}
while文が適している場合
- 終了条件が不明確な場合
// ユーザーが「終了」と入力するまで続ける
let input = "";
while (input !== "終了") {
input = prompt("コマンドを入力してください(終了するには「終了」と入力):");
// 入力された内容を処理
}
- 条件が満たされるまで続ける場合
// ランダムな数が10以上になるまで生成
let number = 0;
while (number < 10) {
number = Math.random() * 20;
console.log(`生成された数: ${number}`);
}
- ファイル読み込みなどの外部リソース処理
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文を使う場合
- 終了条件が不明確な場合
- ユーザー入力の処理
- 条件が満たされるまで続ける処理
どちらを選ぶかは、以下の点を考慮して判断しましょう:
-
処理の性質
- 回数が決まっているか
- 条件で終了するか
- データ構造に依存するか
-
可読性
- どちらがコードの意図を明確に表現できるか
- メンテナンスのしやすさ
-
パフォーマンス
- 処理の効率性
- メモリの使用
適切なループ文を選択することで、より効率的で読みやすいコードを書くことができます。
次のステップ
ループ処理の基本を理解したら、以下の記事も参考にしてください:
Discussion
最近だと
for await ... of も便利ですぞ
おっ、良い指摘ですね!🎯
for await ... of
は非同期処理で便利ですよね!Promise が含まれるデータの逐次処理にはもってこいです💡たとえば、API から複数のデータを順番に取得するときに
for await ... of
を使うと、シンプルに書けますね👇非同期処理をうまく使いこなせると、コードの可読性もグッと上がりますよね!
また面白い使い方があれば、ぜひシェアしてください✨
setTimeout をPromise に変換して await するように
setInterval を asyncIterator に変換して for await ... of するとネストを浅く保てて良き
(勿論 setTimeout を 使って generator 書いて for await ... of するほうが簡単ではありますが