intellista

engineer's notes about application development, data analysis, and so on

三項演算子で条件分岐の代入・返却をシンプルに!


こんにちは!
三項演算子を使っていますか?

三項演算子といえば、単純な条件分岐をシンプルに書けるメリットがある一方、頭ごなしに「わかりにくい」といった批判されることもある、賛否両論の構文です。

三項演算子は、きちんとメリットを理解すれば、効果的に可読性を確保できる、非常に強力な文法です。
今回は、私の好きな三項演算子について、全力で使いどころをご紹介します!

三項演算子の使いどころ

三項演算子の使いどころを説明します。

私は次の3つをすべて満たす処理であれば、三項演算子を必ず使います。

  • 異なる処理をするのではなく、異なる値を返すのみの条件分岐
  • 条件が変数 == 値といったシンプルな比較
  • 各条件の変数が同一 (つまりswitch文を使える場合)

例えば、次のような処理です。

    let code = 
        color === "black"  ? 0 :
        color === "red"    ? 1 :
        color === "yellow" ? 2 :
        color === "blue"   ? 3 :
        color === "green"  ? 4 :
        0; // default

(コードの例はJavaScriptですが、大体どの言語でも同じです。)

上述の「3つをすべて満たす処理」の1つ目に、次のものがありました。

  • 異なる処理をするのではなく、異なる値を返すのみの条件分岐

この「異なる値を返す」とは、上のコードのように三項演算の結果を代入したり返却したりすることです。

使いどころは限定されますが、実際のアプリケーション開発では使うシーンが多いです。

上述の「3つをすべて満たす処理」の3つ目に、次のものがありました。

  • 各条件の変数が同一 (つまりswitch文を使える場合)

同じことをswitch文で書いてみます。

    let code = 0; // default
    switch(color) {
      case "black":
        code = 0;
        break;
      case "red":
        code = 1;
        break;
      case "yellow":
        code = 2;
        break;
      case "blue":
        code = 3;
        break;
      case "green":
        code = 4;
        break;
    }

意味は同じです。
でも、なんだか冗長ではありませんか?
しかも読みづらいです。
前述の三項演算子の例なら7行ですが、switch文の例だと18行と2倍以上です。

逆に、こういったswitch文を見かけたら三項演算子に置き換えてしまった方が幸せです。
冗長な実装は可読性やメンテナンス性を損ない、バグを生みやすくなったり開発スピードが落ちたり、いいことがありません。
(中身や影響を理解できず規模で良し悪しを評価する、昔ながらのひとたちにはその瞬間ウケがよいかもしれませんが・・・)

同じことを書くなら、絶対シンプルな方がいいです。

ちなみに、この例の場合 let code = 0;の変数宣言でデフォルト値をセットしているため、switch文のdefault:句は不要ですので書いていません。
なお、この let code = 0;という変数宣言も、三項演算子なら不要になりとてもシンプルです。

ついでにif文で同じことを書いたらどうなるか見てみます。

    let code = 0; // default
    if (color === "black") {
      code = 0;
    } else if (color === "red") {
      code = 1;
    } else if (color === "yellow") {
      code = 2;
    } else if (color === "blue") {
      code = 3;
    } else if (color === "green") {
      code = 4;
    }

意味はわかりますが、変数colorがすべての条件で同一ですので、if文ではなくswitch文を使うべきシーンです。

ちなみに、if文の処理が1行の場合は、{}を除去できますよね。
好き嫌い(というかプロジェクトのコーディング基準で禁止されている場合も)あるかもしれませんが、除去するともっとシンプルになります。

    let code = 0; // default
    if (color === "black") code = 0;
    else if (color === "red") code = 1;
    else if (color === "yellow") code = 2;
    else if (color === "blue") code = 3;
    else if (color === "green") code = 4;

これだと、三項演算子の例に近いですね。
でもこう書くくらいなら、やはり三項演算子のほうがスッキリします。
switch文と同様に、 let code = 0;という変数宣言も、三項演算子なら不要になりとてもシンプルです。

三項演算子がないPythonでは?

Pythonには三項演算子がありません。
と、いわれることもあり本当ですが、実態としてはあります。

Pythonではifキーワード(if文ではありません)を使って三項演算を表現します。
(個人的には、素直に三項演算子でいいと思うのですが・・・)

JavaScriptの例では次のように書きました。

    let code = 
        color === "black"  ? 0 :
        color === "red"    ? 1 :
        color === "yellow" ? 2 :
        color === "blue"   ? 3 :
        color === "green"  ? 4 :
        0; // default

Pythonで同じことを書くと、次のようになります。

code = 0 if color == 'black' \
    else 1 if color == 'red' \
    else 2 if color == 'yellow' \
    else 3 if color == 'blue' \
    else 4 if color == 'green' \
    else 0

JavaScriptの例とほとんど同じですね。
活用できると思います。

なお、Pythonの行末の\は、ソースコードの途中で改行するための記号です。
試しに改行しないで同じことを書いてみます。

code = 0 if color == 'black' else 1 if color == 'red' else 2 if color == 'yellow' else 3 if color == 'blue' else 4 if color == 'green' else 0

やりたいことがわかっているので何とか読み解けるかもしれませんが、初見なら、もはや呪文ですよね。
ソースコードは効果的に改行して、メンテナンス性を確保したいものです。

なお、Pythonは自由に改行するとたいてい怒られます。
怒られたら\を入れましょう。

switch文の方が有利な場合

三項演算子よりもswitch文のほうがスッキリと書ける場合があります。
それは、複数の条件で同じ値を返す場合です。

例えば、三項演算子では次のような処理になるとします。

    let code = 
        color === "black"  ? 0 :
        color === "gray"   ? 0 :
        color === "red"    ? 1 :
        color === "pink"   ? 1 :
        color === "yellow" ? 2 :
        color === "amber"  ? 2 :
        color === "orange" ? 2 :
        color === "blue"   ? 3 :
        color === "indigo" ? 3 :
        color === "cyan"   ? 3 :
        color === "green"  ? 4 :
        color === "teal"   ? 4 :
        color === "lime"   ? 4 :
        0; // default

同じことをswitch文で書いてみます。

    let code = 0; // default
    switch(color) {
      case "black":
      case "gray":
        code = 0;
        break;
      case "red":
      case "pink":
        code = 1;
        break;
      case "yellow":
      case "amber":
      case "orange":
        code = 2;
        break;
      case "blue":
      case "indigo":
      case "cyan":
        code = 3;
        break;
      case "green":
      case "teal":
      case "lime":
        code = 4;
        break;
    }

まぁ・・・正直どっこいどっこい(というかこの例では読みづらい?)かもしれません。
ですが、同値のラベルが大量になればなるほど、switch文が短くなります。

ちなみに、フォールスルー(fall through; case句で明示的にbreakしなければbreakせずに次のcase句の処理に移ること)がコンパイルエラーになる言語「C#」でも、ラベルのみなら上記のようにフォールスルー可能です。

なお、Java言語はもっと強力に進化しており、はJava14(JDK14)以降で、次のように書けます。

    var code = 0; // default
    switch(color) {
      case "black", "gray":
        code = 0;
        break;
      case "red", "pink":
        code = 1;
        break;
      case "yellow", "amber", "orange":
        code = 2;
        break;
      case "blue", "indigo", "cyan":
        code = 3;
        break;
      case "green", "teal", "lime":
        code = 4;
        break;
    }

ちなみに、Javaswitch文が文字列に対応したのはJava1.7(Java7)以降(遅...)なので注意が必要です。
また、varという型宣言は、Java10で導入されたローカル変数の型推論です。(この例では従来のintでもいいです。)

さらにJava14ではswitch文の進化がとまらず、次のようにも書けます。

    var code = switch(color) {
      case "black", "gray" -> 0;
      case "red", "pink" -> 1;
      case "yellow", "amber", "orange" -> 2;
      case "blue", "indigo", "cyan" -> 3;
      case "green", "teal", "lime" -> 4;
      default -> 0;
    }

これはswitch文から進化した派生のswitch式と呼ばれる構文です。

むむっ...これでようやく三項演算子よりもシンプルになった気がします...!
でも条件が増えたり減ったりするならカンマ区切りよりも行をわけたほうがメンテナンスが楽な気もします(悩ましい...)。
Javaでしたらぜひ、ご検討ください。
というか、もしかしてこれって、あくなき言語仕様の闘いなのでしょうか...

三項演算子を使ってはいけない処理

三項演算子を使ってはいけない処理は、次のいずれかを満たす処理です。

  • 異なる処理をする条件分岐
  • 条件が変数 == 値ではない複雑な比較
  • 各条件の変数がバラバラ (つまりswitch文が使えない場合)

これらは、最初に説明した三項演算子の使いどころ(下記)の裏返しです。

  • 異なる処理をするのではなく、異なる値を返すのみの条件分岐
  • 条件が変数 == 値といったシンプルな比較
  • 各条件の変数が同一 (つまりswitch文を使える場合)

例は省略しますが、「三項演算子を使ってはいけない処理」の実装を無理やり三項演算子で書こうとしても、書けないか、ものすごく読みづらくなります。

目安としては、次の例に似たような処理のみ三項演算子を使うのが無難です。

    let code = 
        color === "black"  ? 0 :
        color === "red"    ? 1 :
        color === "yellow" ? 2 :
        color === "blue"   ? 3 :
        color === "green"  ? 4 :
        0; // default

まとめ

今回は、私の好きな三項演算子について、全力で使いどころをご紹介しました。

今回の例では次のように代入の例としました。

    let code = 
        color === "black"  ? 0 :
        color === "red"    ? 1 :
        color === "yellow" ? 2 :
        color === "blue"   ? 3 :
        color === "green"  ? 4 :
        0; // default

ですが、三項演算は値を返却する構文ですので、次のような返却(return)でも使えてスッキリします。

    return  color === "black"  ? 0 :
            color === "red"    ? 1 :
            color === "yellow" ? 2 :
            color === "blue"   ? 3 :
            color === "green"  ? 4 :
            0; // default

JavaScriptの場合return直後に改行するとうまく動かないので、returnの後に構文を続けてください。

効果的に可読性を確保できる非常に強力な文法「三項演算子」をご活用いただき、開発や分析での実装に役に立てちましたら嬉しいです!