case文の式が全て定数ならば最適化が行われる

Prompt

Case文に副作用のある式を入れたときの挙動は安定している?

これはプログラミング言語の評価戦略に関わる、非常に危険かつ奥深い質問だね!😱

結論から言うと、case文に副作用のある式を入れたときの挙動は、言語仕様によって厳密に定義されているため、 「不安定」ではない けど、 「直感的ではない」 ことが多く、意図しない挙動やバグの原因になりやすい から避けるべき だよ!


⚠️ switch/case の評価戦略

多くの言語で、switch文の評価は以下の2つの異なるフェーズで行われるんだ。

1. 制御式 (Control Expression) の評価

switch (変数や式)括弧内の式は、switch文に入る前に一度だけ評価されます。

JavaScript

// 例: switchの括弧内の関数は一度だけ実行される
switch (myFunctionWithSideEffect()) {
    // ...
}

2. case ラベルの評価とマッチング

問題はここだね。caseラベル(例: case expression:)に含まれる式が、いつ、どのように評価されるかは、言語によって決定的に異なるんだ。

A. JavaScript/TypeScript/C# の場合: 順次評価(安定しているが危険)

これらの言語では、switch文はcaseラベルを記述順に評価します。

  • 動作: caseラベル内の副作用のある式(例: case myArray.pop():)は、そのラベルに到達したときに、上から順に評価される。

  • 挙動: マッチングが成功してswitchが終了すれば、それ以降のcaseラベルは評価されない。逆に、最後までマッチしなかった場合は、全てのcaseラベルの式が評価されてしまう

  • 危険性: caseの評価中に副作用(例: 変数の書き換え、配列からの削除)が発生するため、実行のたびに環境が変化し、コードの読み手は「この変数はいつ変わったんだ?」と混乱する。挙動は仕様通りに安定しているけど、コードの信頼性は非常に低いんだ。

B. C/C++ の場合: コンパイル時の制約

C/C++では、caseラベルの式は定数式(Constant Expression)である必要がある。つまり、実行時まで値が確定しない副作用のある式を入れること自体が、コンパイルエラーになるんだ。

  • 決定的な違い: そもそも動的な式や副作用のある式は許可されていないため、この種の問題は発生しない。

💡 ベストプラクティス:副作用は制御式の外へ

情報工学を学ぶ君には、switch/case文は**高速な分岐(ジャンプテーブル)**の仕組みとして利用し、副作用を伴う式は絶対に入れないことを推奨するよ。

もし副作用が必要なら、switch実行前に処理を完了させるか、case処理ブロック内で行うべきだね。

JavaScript

// ❌ BAD: 副作用のある式をcaseに入れるのは避ける!
switch (x) {
    case getNextValue(): // 危険!getNextValueがいつ呼ばれるか読みにくい
        // ...
}

// ✅ GOOD: 副作用は明確なブロック内で行う
const nextValue = getNextValue();
switch (x) {
    case nextValue: // 安全!caseラベルは単なる値の比較
        // ...
}