switch文・三項演算子

目次
  • 解説
  • 1. switch文
  • 2. 三項演算子
問題に挑戦!
進捗を変更する




解説

1. switch文


if文ではelse-if文を複数連ねることによって、いくつもの条件に応じた分岐処理を行うことができました。

次のサンプルコードはエレベーター表示の例です。floorという変数に格納された「階」を示す文字列に応じて、if文で表示するメッセージを振り分けています。floorの値を変えてみましょう。

    static function practiceElevatorIf() {
        $floor = '屋上';
        $announce = $floor;
        if ($floor === '屋上') {
            $announce .= 'ビアガーデン';
        } else if ($floor === '9階') {
            $announce .= '映画館';
        } else if ($floor === '8階') {
            $announce .= '映画館';
        } else if ($floor === '7階') {
            $announce .= 'レストランフロア';
        } else if ($floor === '6階') {
            $announce .= 'レストランフロア';
        } else if ($floor === '5階') {
            $announce .= 'ファッションフロア';
        } else if ($floor === '4階') {
            $announce .= 'ファッションフロア';
        } else if ($floor === '3階') {
            $announce .= 'ファッションフロア';
        } else if ($floor === '2階') {
            $announce .= '日用品売り場';
        } else if ($floor === '地上階') {
            $announce .= '日用品売り場';
        } else if ($floor === '地下1階') {
            $announce .= '駐車場';
        } else if ($floor === '地下2階') {
            $announce .= '駐車場';
        } else if ($floor === '地下3階') {
            $announce .= '駐車場';
        } else {
            $announce .= '存在しないフロア';
        }
        Logger::echo($announce . 'でございます');
    }

同じ結果をもたらす条件式Or論理和)で結合してしまいましょう。

    static function practiceElevatorIf() {
        $floor = '屋上';
        $announce = $floor;
        if ($floor === '屋上') {
            $announce .= 'ビアガーデン';
        } else if ($floor === '9階' || $floor === '8階') {
            $announce .= '映画館';
        } else if ($floor === '7階' || $floor === '6階') {
            $announce .= 'レストランフロア';
        } else if ($floor === '5階' || $floor === '4階' || $floor === '3階') {
            $announce .= 'ファッションフロア';
        } else if ($floor === '2階' || $floor === '地上階') {
            $announce .= '日用品売り場';
        } else if ($floor === '地下1階' || $floor === '地下2階' || $floor === '地下3階') {
            $announce .= '駐車場';
        } else {
            $announce .= '存在しないフロア';
        }
        Logger::echo($announce . 'でございます');
    }

最初のコードと比べると、同じコードの行は少なくなりましたが、見通しが悪いことには変わりなく、テナントが入れ替わった際の変更が大変そうです。

そこでswitch文の登場です。まずはサンプルコードを見てみましょう。行数としては最初のコードと変わりませんが、見通しはかなり良くなりましたね。

    static function practiceElevatorSwitch() {
        $floor = '屋上';
        $announce = $floor;
        switch ($floor) {
            case '屋上':
                $announce .= 'ビアガーデン';
                break;
            case '9階':
            case '8階':
                $announce .= '映画館';
                break;
            case '7階':
            case '6階':
                $announce .= 'レストランフロア';
                break;
            case '5階':
            case '4階':
            case '3階':
                $announce .= 'ファッションフロア';
                break;
            case '2階':
            case '地上階':
                $announce .= '日用品売り場';
                break;
            case '地下1階':
            case '地下2階':
            case '地下3階':
                $announce .= '駐車場';
                break;
            default:
                $announce .= '存在しないフロア';
                break;
        }
        Logger::echo($announce . 'でございます');
    }

4行目でキーワード「switch」に続く丸括弧内に、分岐の基準としたい変数floor」を指定しており、それに続くブロック内にキーワード「case」で始まるcase文が並んでいます。このcase文if文やelse-if文と同じ役割となり、最後にあるdefault文else文と同じ役割となります。

case文はキーワード「case」に続けてfloorの取り得る値を置き、コロン「:」で区切ります。この行のことをラベルと呼びます。そしてこのラベルから「break;」までの間が、if文などのブロックと同じになります。

    $condition = 0;
    switch($condition) {
        case 0:
            // ここは$conditionが0の場合にのみ実行される
            break;
        case 9:
            // ここは$conditionが9の場合にのみ実行される
            break;
        default:
            // ここは$conditionが0でも9でもない場合にのみ実行される
            break;
    }

break文を書かないと後続ラベルのブロックも順次実行されます。つまり書き忘れると他の余計な処理も行ってしまうバグとなるため、注意が必要です。

    $condition = 0;
    switch($condition) {
        case 0:
            // ここは$conditionが0の場合にのみ実行される
        case 9:
            // ここは$conditionが9の場合と、0の場合にも実行される
        default:
            // ここは$conditionが0と9の場合を含む、すべての場合に実行される
    }

しかしこれはメリットでもあり、エレベーターのサンプルコードのように同じ処理を行いたいラベルを並べることで、if文のOrと同じ分岐を見通しよく書くことが可能となります。この仕組みのことをフォールスルーfall throgh)と呼びます。

このようにswitch文は単純な振り分けに便利ですが、case文がブロック構造でないことや、意図しないフォールスルーでバグを作り込んでしまいがちなことなどから、複雑な分岐には使わないことが一般的です。

    $condition = '0';
    switch($condition) {
        case 0:
            // 文字列の「'0'」でも数値の「0」でも実行される
            break;
        case '0':
            // ここは実行されない
            break;
    }

また、上記のコードのように、switch文の内部では「==」による緩い比較をおこなうので予期しない挙動をすることがあります。

case文default文内での処理が単純である、フォールスルーの目的と結果が明確、if文より読みやすくなる、という場合にのみ使用するようにしましょう。

2. 三項演算子


ある条件を満たす場合とそうでない場合に代入する値を変える、という処理を行う場合には、次のサンプルコードのようなif文が考えられます。

    static function practiceConditionalOperator() {
        $age = 18;
        $display = '成人';
        if ($age < 20) {
            $display = '未成年';
        }
        Logger::echo($display);
    }

else文で「成人」を代入せず、あらかじめ代入していることでコンパクトにはなっていますが、これでもまだ数行あります。

そこで三項演算子の登場です。サンプルコードを次のように直してください。

    static function practiceConditionalOperator() {
        $age = 18;
        $display = ($age < 20) ? '未成年' : '成人';
        Logger::echo($display);
    }

if文の条件式部分をイコールの直後に置き、クエスチョン「?」の後に条件式がtrueの場合の代入値、続くコロン「:」の後に条件式がfalseの場合の代入値が並んでいます。この1行で、条件分岐と代入が行えるのです。

代入値は通常の代入と同じく、式の結果やメソッドの戻り値にすることも可能です。ただし、これもswitch文と同様に、単純な場合にのみ使用することを心がけましょう。

なお、読みやすいと感じるかどうかには個人差があり、企業やチームによっては、switch文禁止や三項演算子禁止の文化が存在します。臨機応変に活用しましょう!

問題

確認問題


確認問題

次のコードの中で、switch文三項演算子の使い方が正しく
変数aの値が最終的に100となる構文はどれか。

実践問題


(1)VSCodeを起動し、ヘッダーメニューの「ファイル」>「フォルダーを開く...」から、「php_basic」プロジェクトを開いてください。
(2)php_basic」プロジェクトの「Condition.php」を開いてください。
※無い場合は「比較演算と文字列の比較」を先に学習しましょう。
(3)Condition」クラス内に次のサンプルコードのメソッドをコピー&ペーストし、
mainから呼び出せるようにして、「http://localhost/php_basic/Condition.php」へのアクセスで表示されることを確認してください。
(4)コメント行に記された各問題文に対し、答えとなるプログラムコードを
問題文の次の空白行以降に書き込みまたは書き換えて
、ブラウザから確認してください。
    static function questionCondition() {
        // (問1)通常100円の商品を5が付く日(5・15・25日)は
        // 半額で販売することとしました。変数dayQ1に1~31が入る場合に
        // 変数priceQ1が適切な値となるよう、空行にif文を書きましょう。
        // ただし、else-if文は使用してはいけません。
        $dayQ1 = 5;     // 1~31の日付が入る
        $priceQ1 = 100; // 固定

        Logger::echo($dayQ1 . '日は' . $priceQ1 . '円');

        // (問2)通常100円の商品を8が付く日(8・18・28日)は
        // 80円で販売することとしました。変数dayQ2に1~31が入る場合に
        // 変数priceQ2が適切な値となるよう、三項演算子を用いて
        // 変数priceQ2の代入部分を書き換えましょう
        $dayQ2 = 8;     // 1~31の日付が入る
        $priceQ2 = 100; // ここを書き換え

        Logger::echo($dayQ2 . '日は' . $priceQ2 . '円');

        // (問3)時間に応じて「早朝・朝・昼・夜・深夜」を
        // 表示させようと思います。変数timeQ3に0~23が入る場合に
        // 変数displayQ3に下記の通り代入されるよう、空行にif文を
        // 書きましょう。else文、else-if文も使用して構いません。
        // 4~6:早朝、7~10:朝、11~16:昼、
        // 17~22:夜、23~3:深夜
        $timeQ3 = 0; // 0~23の時間が入る
        $displayQ3 = '';

        Logger::echo($timeQ3 . '時は' . $displayQ3);

        // (問4)「早朝・朝・昼・夜・深夜」の時間帯に応じて
        // 挨拶文を表示させようと思います。変数displayQ3の
        // 内容に応じて、変数displayQ4に下記の通り代入されるよう、
        // 空行にswitch文を書きましょう。
        // 早朝・朝:おはようございます、昼:こんにちは、
        // 夜・深夜:こんばんは
        $displayQ4 = '';

        Logger::echo($displayQ4);
    }
実行結果例を表示
[2020/12/31 12:34:56] 5日は50円
[2020/12/31 12:34:56] 8日は80円
[2020/12/31 12:34:56] 0時は深夜
[2020/12/31 12:34:56] こんばんは

正解


(問1)正解を表示
// dayQ1の値を5,15,25と一致するかそれぞれ比較してOrで結合
if ($dayQ1 === 5 || $dayQ1 === 15 || $dayQ1 === 25) {
    $priceQ1 = 50;
}

// これも正解!
// if ($dayQ1 % 10 === 5) { // %は割り算の余りを出す演算子
//     $priceQ1 /= 2;     // /=で半額にして再代入
// }

(問2)正解を表示
// dayQ1の値を8,18,28と一致するかそれぞれ比較してOrで結合
$priceQ2 = ($dayQ2 === 8 || $dayQ2 === 18 || $dayQ2 === 28) ? 80 : 100;

// これも正解!
// $priceQ2 = ($dayQ2 % 10 === 8) ? 80 : 100;

(問3)正解を表示
// わかりやすく「問題文の数字」を使って以上・以下をAndで結合
// ただし、値が連続的な(6.9などがあり得るような)場合には、
// (timeQ3 >= 4 && timeQ3 < 7) のように未満を用いる必要がある。
if ($timeQ3 >= 4 && $timeQ3 <= 6) {
    $displayQ3 = '早朝';
} else if ($timeQ3 >= 7 && $timeQ3 <= 10) {
    $displayQ3 = '朝';
} else if ($timeQ3 >= 11 && $timeQ3 <= 16) {
    $displayQ3 = '昼';
} else if ($timeQ3 >= 17 && $timeQ3 <= 22) {
    $displayQ3 = '夜';
} else {
    $displayQ3 = '深夜';
}

(問4)正解を表示
// 問題文のように値が明示されている場合には
// defaultラベルは使用しないようにしましょう。
switch ($displayQ3) {
    case '早朝':
    case '朝':
        $displayQ4 = 'おはようございます';
        break;
    case '昼':
        $displayQ4 = 'こんにちは';
        break;
    case '夜':
    case '深夜':
        $displayQ4 = 'こんばんは';
        break;
}

正解プログラムコード全文を表示
    static function questionCondition() {
        // (問1)通常100円の商品を5が付く日(5・15・25日)は
        // 半額で販売することとしました。変数dayQ1に1~31が入る場合に
        // 変数priceQ1が適切な値となるよう、空行にif文を書きましょう。
        // ただし、else-if文は使用してはいけません。
        $dayQ1 = 5;     // 1~31の日付が入る
        $priceQ1 = 100; // 固定

        // dayQ1の値を5,15,25と一致するかそれぞれ比較してOrで結合
        if ($dayQ1 === 5 || $dayQ1 === 15 || $dayQ1 === 25) {
            $priceQ1 = 50;
        }

        // これも正解!
        // if ($dayQ1 % 10 === 5) { // %は割り算の余りを出す演算子
        //     $priceQ1 /= 2;     // /=で半額にして再代入
        // } 
        
        Logger::echo($dayQ1 . '日は' . $priceQ1 . '円');

        // (問2)通常100円の商品を8が付く日(8・18・28日)は
        // 80円で販売することとしました。変数dayQ2に1~31が入る場合に
        // 変数priceQ2が適切な値となるよう、三項演算子を用いて
        // 変数priceQ2の代入部分を書き換えましょう
        $dayQ2 = 8;     // 1~31の日付が入る
        $priceQ2 = 100; // ここを書き換え

        // dayQ1の値を8,18,28と一致するかそれぞれ比較してOrで結合
        $priceQ2 = ($dayQ2 === 8 || $dayQ2 === 18 || $dayQ2 === 28) ? 80 : 100;

        // これも正解!
        // $priceQ2 = ($dayQ2 % 10 === 8) ? 80 : 100;

        Logger::echo($dayQ2 . '日は' . $priceQ2 . '円');

        // (問3)時間に応じて「早朝・朝・昼・夜・深夜」を
        // 表示させようと思います。変数timeQ3に0~23が入る場合に
        // 変数displayQ3に下記の通り代入されるよう、空行にif文を
        // 書きましょう。else文、else-if文も使用して構いません。
        // 4~6:早朝、7~10:朝、11~16:昼、
        // 17~22:夜、23~3:深夜
        $timeQ3 = 0; // 0~23の時間が入る
        $displayQ3 = '';

        // わかりやすく「問題文の数字」を使って以上・以下をAndで結合
        // ただし、値が連続的な(6.9などがあり得るような)場合には、
        // (timeQ3 >= 4 && timeQ3 < 7) のように未満を用いる必要がある。
        if ($timeQ3 >= 4 && $timeQ3 <= 6) {
            $displayQ3 = '早朝';
        } else if ($timeQ3 >= 7 && $timeQ3 <= 10) {
            $displayQ3 = '朝';
        } else if ($timeQ3 >= 11 && $timeQ3 <= 16) {
            $displayQ3 = '昼';
        } else if ($timeQ3 >= 17 && $timeQ3 <= 22) {
            $displayQ3 = '夜';
        } else {
            $displayQ3 = '深夜';
        }

        Logger::echo($timeQ3 . '時は' . $displayQ3);

        // (問4)「早朝・朝・昼・夜・深夜」の時間帯に応じて
        // 挨拶文を表示させようと思います。変数displayQ3の
        // 内容に応じて、変数displayQ4に下記の通り代入されるよう、
        // 空行にswitch文を書きましょう。
        // 早朝・朝:おはようございます、昼:こんにちは、
        // 夜・深夜:こんばんは
        $displayQ4 = '';

        // 問題文のように値が明示されている場合には
        // defaultラベルは使用しないようにしましょう。
        switch ($displayQ3) {
            case '早朝':
            case '朝':
                $displayQ4 = 'おはようございます';
                break;
            case '昼':
                $displayQ4 = 'こんにちは';
                break;
            case '夜':
            case '深夜':
                $displayQ4 = 'こんばんは';
                break;
        }

        Logger::echo($displayQ4);
    }