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ラベルは単なる値の比較
// ...
}