三項演算子は有害と考えられますか?[閉まっている]


79

たとえば、このワンライナーを好むだろうか

int median(int a, int b, int c) {
    return (a<b) ? (b<c) ? b : (a<c) ? c : a : (a<c) ? a : (b<c) ? c : b;
}

または複数のreturnステートメントを含むif / elseソリューションですか?

?:適切な場合と適切でない場合 初心者に教えたり隠したりするべきですか?


221
この特定の使用法は:)
karmajunkie

6
誰がそれをコーディングし、4つの数値の中央値のバージョンはどのように見えますか?それとも5?
メイソンウィーラー

3
より正しい名前は「条件演算子」です。たまたま使用中の最も一般的な三項演算子です。
アランピアース

1
これは2年以上前にstackoverflowで尋ねられました。ここですべてを再確認しますか?stackoverflow.com/questions/160218/to-ternary-or-not-to-ternary
webbiedave

3
なぜこのような質問が出てくるのか、私は驚いています。答えは常に「動作し、読みやすいものは何でも」です。-最後のものも同様に重要です。
Apoorv Khurasia

回答:


234

三項演算子は悪ですか?

いいえ、それは祝福です。

いつですか?:適切ですか?

とても単純なもので、多くの行を無駄にしたくない場合。

それはいつですか?

コードの読みやすさと明確さが損なわれ、注意が不十分なためにミスが発生する可能性が高くなった場合(たとえば、例のように多くのチェーン演算子を使用した場合)。


リトマステストは、長い目で見ればコードの読み取りと保守が容易であることを疑い始めるときです。それをしないでください。


23
+1は、コードの可読性と明瞭さが損なわれる場合に使用します。あなたの例のように、多くの連鎖演算子があります。 この例は、同等のif / elseよりも理解に時間がかかります。
10

23
素晴らしい説明のためにブラボー+1!開発者は、あるものが判断の呼び出しであることに気付かない傾向があり、すべてが白黒であることを望みます。それは私を夢中にさせます。私は「Xは悪だ、決して使わない」という意見の多くの人々に出会った。「Xの得意なところにXを使用すれば素晴らしい」と思います。
モニカを

54
また、使用すると悪です:myVar =(someExpression)?真偽; ああ!
10

20
@adamk:悪のためにこれを試してください:myVar = someExpression ? false : true;
ディーン・ハーディング

8
どうですか(someExpression ? var1 : var2)++:-)
fredoverflow

50

ネストされていない3項演算子(つまり、1回だけ使用されるステートメント)は問題ないと思いますが、2つ以上をネストしている場合、読みにくくなります。


3
これは単純化しすぎと見なされる場合がありますが、従うのは非常に簡単なガイドラインであり、ほとんどの場合に機能します。
アラン・ピアース

2
それは実際に私の経験則です:あなたはそれらをネストするべきではありません。
ピオベザン14年

それらを使用し、ネストする場合は、人類の愛のために、括弧と空白を使用して読みやすくします。if-elseも同じようにmadeいものにできます。コンパイラーは読むことはできるが人間はできないことを書くことで、あなたがどれだけ「賢い」かを見せびらかす衝動に抵抗してください。いつかあなたはできない人間になるでしょう。
candied_orange 14

24

いつですか?:適切

  • コードがより簡潔で読みやすくなるとき。

それはいつですか?

  • コードが読めなくなるとき。
  • コードを保守しなければならない人ではなく、ReSharperのようなリファクタリングツールを喜ばせるためだけにやっているなら

三項式内にロジックまたは関数呼び出しがある場合、それを見るのが恐ろしくなります。


これは多くの賛成に値します!
ピオベザン14年

22

(私が思うに)誰も指摘していない違いの1つは、if-elseが値を返せないのに対し、三項演算子は返せるということです。

F#から来て、三項演算子を使用してパターンマッチングを模倣することが好きな場合があります。

match val with
| A -> 1
| B -> 3
| _ -> 0

return val == A ? 1 : 
       val == B ? 3 : 
       0;

それはいいね。考えたこともありません。
宮坂

+1 @Benjol:同じことを指摘していました(F#では、すべてがif / elif / elseを含む式です)。私もあなたの例のように三項を使用していますが、これまでのところ未発見です:)。私がやってきたもう1つのことは、おそらくJavascriptで、var res = function() {switch(input) {case 1: return "1"; case 2: return "2"; ...}}()スイッチを式としてエミュレートすることです。
スティーブンスウェンセン

@Stephenは、私は言葉を使用するつもりだったexpressionstatement:)が、私はいつも、私は彼らにラウンド間違った方法を取得し、自分自身の馬鹿を作ってあげる心配です
Benjol

@ベンジョル:あなたの言うことは知っている!
スティーブンスウェンセン

6
また、const後で変更できない変数を初期化するためにCおよびC ++で役立ちます。
デヴィッドソーンリー

13

有効な使用例(IMHO):

printf("Success in %d %s\n", nr_of_tries, (nr_of_tries == 1 ? "try" : "tries"));

これにより、2つの個別のprintステートメントを使用するよりもコードが読みやすくなります。入れ子になった例は次のとおりです:(わかりやすい?はい:いいえ)


11
これを行うと、アプリケーションをローカライズしなければならない人にとっては、それが地獄になってしまうことに留意してください。もちろん、それが問題でない場合は、すぐに進めてください。
アノン。

1
これが私の経験則です。1レベルのネスト(状況によっては)。
オリバーワイラー

7

絶対に悪ではありません。実際、それは純粋で、if-then-elseはそうではありません。

Haskell、F#、MLなどの関数型言語では、悪と見なされるのはif-then-elseステートメントです。

その理由は、命令型if-then-elseステートメントのような「アクション」では、変数宣言をその定義から分離する必要があり、関数に状態を導入するためです。

たとえば、次のコードでは:

const var x = n % 3 == 1
    ? Parity.Even
    : Parity.Odd;

Parity x;
if (n % 3 == 1)
    x = Parity.Even;
else
    x = Parity.Odd;

最初のものには、短くなること以外に2つの利点があります。

  1. x は定数であるため、バグが発生する可能性ははるかに低く、2番目の方法では不可能な方法で最適化できます。
  2. 型は式によって明確にされるため、コンパイラーはそれxがtypeである必要があると簡単に推測できますParity

紛らわしいことに、関数型言語では、三項演算子はしばしばif-then-elseと呼ばれます。Haskellでは、と言うかもしれませんx = if n mod 3 == 1 then Odd else Even


うん、これも@Benjolが作成したポイントです。switch文をJavascriptの式としてエミュレートする楽しい方法については、彼の答えに対する私のコメントをご覧ください。
スティーブンスウェンセン

7

その特定の表現は私の目を傷つけます。メンテナンスできないので、それを使用した私のチームの開発者を非難します。

三項演算子は、うまく使えば悪ではありません。それらは単一行である必要さえありません。適切にフォーマットされた長いものは、非常に明確で理解しやすいものです。

return
      ( 'a' == $s ) ? 1
    : ( 'b' == $s ) ? 2
    : ( 'c' == $s ) ? 3
    :                 4;

私は同等のif / then / elseチェーンよりもそれが好きです:

if ( 'a' == $s ) {
    $retval = 1;
}
elsif ( 'b' == $s ) {
    $retval = 2;
}
elsif ( 'c' == $s ) {
    $retval = 3;
}
else {
    $retval = 4;
}

return $retval;

それらを次のように再フォーマットします。

if    ( 'a' == $s ) { $retval = 1; }
elsif ( 'b' == $s ) { $retval = 2; }
elsif ( 'c' == $s ) { $retval = 3; }
else                { $retval = 4; }

return $retval;

条件と割り当てが簡単に調整できる場合。それでも、私はそれが短く、条件と割り当ての周りにそれほどノイズがないので、三元バージョンを好みます。


なぜコメントに改行を入れられないのですか?ああ!
クリストファーマハン

3

VS.NETのReSharperif...else?:オペレーターに置き換えることを提案する場合があります。

ReSharperは、条件/ブロックが特定の複雑度レベルを下回っている場合にのみ提案するようif...elseです。


4
私はReSharperの約愛する別の素晴らしい機能
匿名タイプ

2

これは、if / elseの組み合わせと同じように見栄えがするように再フォーマットできます。

int median(int a, int b, int c)
{
    return
        (a<b)
        ?
            (b<c)
            ? b
            :
                (a<c)
                ? c
                : a
        :
            (a<c)
            ? a
            :
                (b<c)
                ? c
                : b;
}

しかし、問題は、実際に何が起こるかを表すためのインデント権を取得したかどうか、私には本当にわからないことです。:-)


3
+1これは同等のif-else構造よりもはるかに優れていると思います。キーはフォーマットです。
オーブリング

13
これをコードベースで見た場合、新しい仕事を探すことを真剣に検討します。
ニックラーセン

+1この実際のインデントではありませんが、これはこの例の最良の解決策です。
マークハード

2
別の偶発的な自動書式設定によって、そのインデントがすべて
消去され

2

悪であるとはほど遠く、三項演算子は天の恵みです。

  • ネストされた式で決定を行う場合に最も便利です。典型的な例は関数呼び出しです:

    printf("I see %d evil construct%s in this program\n", n, n == 1 ? "" : "s");
    
  • あなたの特定の例では、三項はaの下のトップレベルの式であるため、ほとんど無償returnです。returnキーワード以外のものを複製することなく、条件をステートメントレベルに引き上げることができます。

注:中央値の特定のアルゴリズムを読みやすくするものはありません。


あなたがそれをより良くすることができないほど読みやすいのは難しいです。printf("I see %d evil construct%s in this program\n", n, "s" unless (n == 1) "s");
パセリエ

2
  1. 「悪」の議論は別として、私の経験では、プログラマーの三項演算子の使用と、コードベース全体の読み取り、追跡、および維持が困難である可能性(完全に文書化されていない場合を除く)の間に高い相関があることがわかりました。プログラマが自分のコードを理解できる人よりも数文字の1〜2文字の行を保存することに関心がある場合、3項ステートメントを理解する小さな混乱は通常氷山の一角です。

  2. 三項演算子は、s ** tがハエを引き付けるように、マジックナンバーを引き付けます。

特定の問題を解決するためにオープンソースライブラリを探していて、そのライブラリの候補に元のポスターの三項演算子などのコードを見た場合、警告ベルが頭の中で鳴り始め、先に進むことを検討し始めました借りる他のプロジェクトに


2

ここでは、それは場合の例だある悪:

oldValue = newValue >= 0 ? newValue : oldValue;

紛らわしくて無駄です。コンパイラーは2番目の式(oldValue = oldValue)を最適化できますが、なぜ最初にコーダーがこれを行ったのですか?

もう一つのすごい:

thingie = otherThingie != null ? otherThingie : null;

一部の人々は単にコーダーになることを意図していない...

グレッグは、同等のifステートメントは「うるさい」と言います。うるさく書いた場合です。ただし、次のように記述できる場合は同じです。

if ('a' == $s) return 1;
if ('b' == $s) return 2;
if ('c' == $s) return 3;
return 4;

これは3項よりもノイズが多いわけではありません。三項の近道かしら?すべての式が評価されますか?


あなたの第二の例は、を思い出させるif (x != 0) x = 0;...
fredoverflow

2

悪の?見て、彼らはただ違うだけです。

ifステートメントです。(test ? a : b)式です。それらは同じものではありません。

値を表現する式が存在します。アクションを実行するためのステートメントが存在します。式はステートメント内に表示できますが、その逆はできません。したがって、合計の項やメソッドの引数など、他の式内で3項式を使用できます。する必要はありません望むならできます。それには何の問題もありません。一部の人々はそれが悪だと言うかもしれませんが、それは彼らの意見です。

三項式の値の1つは、trueとfalseの両方のケースを処理できるようにすることです。ifステートメントはしません。

読みやすさを心配している場合は、読みやすい形式にすることができます。

どういうわけか「悪」がプログラミング用語に忍び込んできました。誰が最初に落としたのか知りたいです。(実際、私は容疑者を持っています-彼はMITにいます。)私はむしろ、この分野での価値判断の客観的な理由があり、人々の好みや名前を呼ぶだけではありません。


1
容疑者が誰であるかについてのヒントがありますか?フィールドに関する知識を少しだけ向上させるためです。
mlvljr

1
@mlvljr:まあ私は間違っているかもしれないので、しないほうがいい。
マイクダンラベイ

1

場所があります。私は、開発者のスキルレベルがひどいものからウィザードのものまで、多くの企業で働いてきました。コードを維持する必要があり、私は永遠にそこにいないので、そこに属するように見えるように書きます(私のイニシャルでコメントを見ずに、あなたができることは非常にまれです変更した場所を確認するために作業したコードを見てください)、そして自分よりもスキルの低い人がそれを維持できることを確認してください。

三項演算子は素晴らしくて格好いいように見えますが、私の経験では、コードの行を維持するのはほぼ不可能です。私の現在の雇用主には、ほぼ20年間出荷されている製品があります。その例はどこにも使用しません。


1

三項演算子は悪だとは思いません。

しかし、ここで私が困惑した落とし穴があります。私は多くの(10歳以上)Cプログラマーでしたが、1990年代後半にWebベースのアプリケーションプログラミングに移行しました。Webプログラマーとして、すぐに三項演算子を持つPHPに出会いました。PHPプログラムにバグがあり、ネストされた三項演算子を含むコード行に最終的に辿りました。PHPの三項演算子は左から右に関連付けられていますが、Cの三項演算子(私が慣れていた)は右から左に関連付けられています。


1

コードの見栄えを悪くするものはすべて悪です。

コードをクリーンにするために3進数を使用する場合は、必ず使用してください。時々phpのように、インライン置換を行うのは素晴らしいことです。例えば

"Hello ".($Male?"Mr":"Ms")." $Name

それは数行を節約し、それはかなり明確ですが、あなたの例は明確にするために少なくともより良い書式設定が必要であり、三項は複数行には本当に良くないので、if / elseを使用することもできます。


1

言えますか?私は、三項演算の悪のこの特定のアプリケーションを見つけることができません。

  1. それが実行する操作は非常に簡単であり、一度それをトラフにすると、いくつかのバグが出る可能性はほとんどありません。
  2. それが何をするかは、関数名に明確に記載されています。
  3. 非常に明白で、将来的には明らかに改善されない何かのために> 1行を取得します(マジックメディアンアルゴリズムが今まで検出されなかった場合を除く)。

慈悲を持ってください、私の評判はすでにかなり哀れです。


1

最大の勝利:アクションのターゲットが1つであることを示します。

if ( $is_whatever )
    $foo = 'A';
else
    $foo = 'B';

フォローできるコードパスは2つあり、読者はどちらの変数が設定されているかを注意深く読む必要があります。この場合、変数は1つだけですが、読者はそれを理解するためにさらに読む必要があります。結局のところ、それはこれだったかもしれません:

if ( $is_whatever )
    $foo = 'A';
else
    $bar = 'B';

三項演算子を使用すると、設定されている変数が1つだけであることは明らかです。

$foo = $is_whatever ? 'A' : 'B';

最も低いレベルでは、最も基本的なのはDRY(Do n't Repeat Yourself)原則です。$foo一度しか指定できない場合は、指定してください。


0

If ... then ... elseは条件を強調する傾向があり、そのため条件付きで実行される操作を強調しません。

三項演算子は逆であり、条件を隠す傾向があり、実行される操作が条件自体よりも重要な場合に有用です。

一部の言語では、1つはステートメントであり、もう1つは式であるため、C ++でconstを条件付きで初期化するため、あまり互換性がないという小さな技術的な問題があります。


0

適切な場合と適切でない場合

同種の人々のために開発する場合、問題はないと思いますが、異なるレベルを処理する人々に対処しなければならない場合、この種のonelinersはコードにさらに複雑なレベルを導入するだけです。したがって、この問題に関する私のポリシーは、コードを短くして123123回説明するのではなく、コードを明確にし、説明しないことです。

初心者に教えたり隠したりするべきですか?

私は初心者に教えられるべきではなく、むしろ必要が出てきたときにそれを理解することを好むので、必要なときにだけ使用され、ifが必要なたびに使用されることはありません。


0

IMO、演算子自体は悪ではありませんが、C(およびC ++)で使用される構文は非常に簡潔です。IMO、Algol 60の方が優れていたため、次のようになりました。

A = x == y ? B : C;

次のようになります(ただし、一般にCのような構文に固執します)。

A = if (x==y) B else C;

それでも、過度に深いネストは読みやすさの問題につながる可能性がありますが、少なくともA)プログラミングを行った人なら誰でも簡単なものを理解でき、B)それを理解している人はかなり深いネストをかなり簡単に処理できます。OTOH、LISP(たとえば)では、a condは3項ステートメントに非常に似ています-ステートメントのセットではなく、値を生成する単一の式です(そして、LISPの大部分はそのようなものです。) )


読みやすくするためだけにこれをしないのはなぜですか?A = (x==y) ? B : C
ジェレミーハイラー

@ジェレミー:かっこが便利だと思う人もいますが、せいぜいあまり役に立たない。数個以上深く入れ子にすると、物事を整理するために(最低限)注意深いインデントが必要です。確かに...同じことがアルゴルに最終的に起こるだろうが、私は、私は、多くの場合、Cで行うようにそれで発生する問題を言うことはありません
ジェリーの棺

私は、三項演算子をネストするのは悪いことだと誰もが同意していると思いました。私はあなたが提供した例について具体的に話していました。特に、ほとんどの言語で最初の言語が2番目の言語に似ている可能性があります。
ジェレミーハイラー

0

定期的に 600〜1200行のメソッド記述するショップは、3進法が「理解しにくい」と言ってはいけません。コードブランチを評価するために5つの条件を定期的に許可しているショップでは、3進数で具体的に要約された条件が「読みにくい」と言わないでください。


0

いつですか?:適切で、いつではないですか?

  • パフォーマンスが向上しない場合は、使用しないでください。コードの可読性に影響します。
  • 一度使用して、ネストしないでください。
  • デバッグが難しくなります。

初心者に教えたり隠したりするべきですか?

重要ではありませんが、「初心者」が学習するのに複雑すぎないため、意図的に隠すべきではありません。


-2

あなたの例では:

def median(a, b, c):
    if a < b < c: return b
    if a < c < b: return c
    if b < a < c: return a
    if b < c < a: return c
    if c < a < b: return a
    if c < b < a: return b

非常に読みやすく、明らかです。<<の間の変数は戻り値です。

更新

同じですが、コードの行が少なくなります。まだシンプルだと思います。

def median(a, b, c):
    if b<a<c or c<a<b: return a
    if a<b<c or c<b<a: return b
    if a<c<b or b<c<a: return c

これには、最悪の場合に12回の比較が必要です
...-fredoverflow

1
おそらく、しかしそれは判読可能です。
クリストファーマハン

-2

constにも必要です

const int nLegs  = isChicken ? 2: 4 ;

奇妙な。私はそのC ++または何かだと思います。constは常にコンパイル時定数(C#のように)だったと
思った-nawfal

@nawfal -あなたが実行時までisChickenがわからない場合は
マーティンベケット

はい、それは何ですか。const特定の言語ではそうだと思います。C#ではconst、コンパイル時に既知の値を常に使用する必要があります。これは意味const int nLegs = isChicken ? 2: 4 ;文句を言わない仕事を、しかしconst int nLegs = true ? 2: 4 ;意志
nawfal
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.