PHP発展編: 繰り返し処理をマスターしよう

繰り返し処理の発展的な内容について
独学エンジニアという学習サイトで学んだ内容を自分用の備忘録としてまとめました。

https://dokugaku-engineer.com/course/serverside

---

## **1. 繰り返し処理の重要性**

繰り返し処理はプログラムで頻繁に使用される構造であり、コードの品質や可読性に大きく影響します。

以下のポイントを押さえることで、より高品質なコードを書けるようになります。

- **適切な繰り返し処理を選択すること**
- **コードを単純にし、複雑さを減らすこと**

---

## **2. 繰り返し処理の選択基準**

PHPには主に以下の繰り返し処理が存在します。それぞれの用途に応じて適切な処理を選びましょう。

1. **配列の全要素に繰り返し処理をしたい場合** → **`foreach`**
2. **決まった回数だけ繰り返し処理をしたい場合** → **`for`**
3. **回数が不明な場合や条件に基づいて繰り返したい場合** → **`while`**

---

## **3. 繰り返し処理を書く際のポイント**

繰り返し処理には「開始時」「本文」「終了時」の3つの段階があります。それぞれで注意すべきポイントを解説します。

---

### **(1) ループの開始時**

1. **初期化コードをループ直前に置く**
    - 使用する変数はループの直前で初期化することで、変数がどこで使われているか把握しやすくなる。
    - **例: 合計金額を求めるコード**
        
        ```php
        $total = 0; // 初期化
        foreach ($items as $item) {
            $total += $item['price'];
        }
        
        ```
        
2. **無限ループには `while (true)` を使う**
    - 無限ループを書く際には `while (true)` を使うと明示的になり、意図が分かりやすい。
    - **悪い例: 無限ループを4文で書く**
        
        ```php
        for ($i = 0; $i < 1000000; $i++) {
            // 終了条件が曖昧
        }
        
        ```
        
    - **良い例: 無限ループを明示的に記述**
        
        ```php
        while (true) {
            // 明確な無限ループ
        }
        
        ```
        

---

### **(2) ループ本文**

1. **ネストを浅くする**
    - ネスト（ループの入れ子）は多くても3段階までに抑える。
    - **悪い例: 深いネスト**
        
        ```php
        foreach ($categories as $category) {
            foreach ($category['items'] as $item) {
                foreach ($item['details'] as $detail) {
                    // 処理
                }
            }
        }
        
        ```
        
    - **良い例: ネストを分割する**
        
        ```php
        foreach ($categories as $category) {
            processCategory($category);
        }
        
        function processCategory($category) {
            foreach ($category['items'] as $item) {
                processItem($item);
            }
        }
        
        ```
        
2. **前処理・後処理をまとめる**
    - ループ中の前処理や後処理は、ループの先頭か末尾にまとめる。
    - **悪い例: バラバラな前処理**
        
        ```php
        foreach ($items as $item) {
            if ($item['price'] > 100) {
                $total++;
            }
            $count++;
        }
        
        ```
        
    - **良い例: 先頭または末尾にまとめる**
        
        ```php
        foreach ($items as $item) {
            $count++;
            if ($item['price'] > 100) {
                $total++;
            }
        }
        
        ```
        
3. **1回のループで1つの機能を実行する**
    - 複数の処理を1つのループでまとめようとせず、必要ならループを分割する。
    - **悪い例: 複数の処理を1つのループで実行**
        
        ```php
        foreach ($items as $item) {
            $total += $item['price'];
            $discountedTotal += $item['price'] * 0.9;
        }
        
        ```
        
    - **良い例: ループを分割**
        
        ```php
        foreach ($items as $item) {
            $total += $item['price'];
        }
        foreach ($items as $item) {
            $discountedTotal += $item['price'] * 0.9;
        }
        
        ```
        
4. **ループの長さを抑える**
    - ループが長すぎると読みにくくなるので、50行以内に抑える。
    - 処理を関数化してループ本体を短くする。

---

### **(3) ループの終了時**

1. **すべてのケースでループが終了することを確認**
    - ループが終了しないケースを防ぐため、脳内で開始条件と終了条件をシミュレーションする。
    - 特に `while` ループでは終了条件を明確にする。
2. **シミュレーションを行う**
    - ループの開始時と終了時の値を想定し、正常に動作するか確認。
    - **例: シミュレーション**
        
        ```php
        $i = 0;
        while ($i < 10) {
            echo $i;
            $i++;
        }
        // $i の値が10になるまでループが終了することを確認。
        
        ```
        

---

## **4. 繰り返し処理を書く際のアプローチ**

### **「内側から外側へ書く」方法**

複雑なループをシンプルに書くための手法です。以下の手順で進めます：

1. **日本語で本文の処理を書く**
    - ループのことは考えずに、処理の本体部分だけを日本語で書き出す。
2. **コードに置き換える**
    - 日本語で書いた処理を実際のコードに変換する。
3. **ループで囲む**
    - 本文を繰り返し処理で囲む。
4. **初期化や終了条件を追記**
    - 必要な変数の初期化や終了条件を追加する。

---

### **具体例: 商品の合計金額を求める**

### **(1) 日本語で本文の処理を書く**

- 「商品から料金を取得する」
- 「料金を合計する」

### **(2) コードに置き換える**

```php
$price = $item['price'];
$total += $price;

```

### **(3) ループで囲む**

- 配列の全要素に繰り返し処理を行うので `foreach` を使用。

```php
foreach ($items as $item) {
    $price = $item['price'];
    $total += $price;
}

```

### **(4) 初期化や終了条件を追記**

- `$items` 配列や `$total` の初期化を追加。

```php
$items = [
    ['name' => '玉ねぎ', 'price' => 100],
    ['name' => 'じゃがいも', 'price' => 200],
    ['name' => 'にんじん', 'price' => 150],
];
$total = 0;

foreach ($items as $item) {
    $price = $item['price'];
    $total += $price;
}

echo "合計金額: {$total}円";

```

---

## **5. 今回のまとめ**

1. **適切な繰り返し処理を選択する**
    - 配列 → `foreach`
    - 回数指定 → `for`
    - 条件判定 → `while`
2. **3つのポイントを押さえる**
    - **開始時**: 初期化をループ直前に行う。
    - **本文**: ネストを浅くし、1つのループで1つの処理に絞る。
    - **終了時**: すべてのケースでループが終了することを確認する。
3. **内側から外側へ書く手法を活用**
    - 本文の処理を日本語で書き、その後ループや初期化を追加する。

---

今回紹介したポイントを意識することで、コードの品質が向上し、バグの発生を防げるようなコードを書けるようにしたいと思いました。
この記事がPHPを学んでいる方の理解を助け、学習の一助になれば幸いです。