三項か三項か?[閉まっている]


186

私は個人的に三項演算子の擁護者です:()?:; 私はそれがその場所にあることを理解していますが、これを使用することに完全に反対している多くのプログラマーや、あまりにも頻繁に使用しているプログラマーに遭遇しました。

どう思いますか?それを使用して見た興味深いコードは何ですか?


22
何かの擁護者は、そのことを支持する人です。
Doug McClean、

8
明確な場合は使用し、混乱した場合は使用しないでください。それは判断の呼びかけです。コードを読みやすくすることができますが、これは単純な式に対してのみです。常にそれ使用しようとすることは、執拗にそれを回避することと同じくらい多くの脅威です。
Abel

3
実際には、それは条件付き演算子です。重複に近い質問は、stackoverflow.com / questions / 725973 /…です。
Daniel Daranas、2011年

私は時々使っていたx = x if x else yが、その後、それは本当にただのx = xまたはy(に減少させること、それについて尋ね、他の助けを借りて実現stackoverflow.com/questions/18199381/self-referencing-ternary/...
だらし

4
このような質問は、建設的なものではありません。
Ungeheuer 2017

回答:


243

単純な式にのみ使用してください:

int a = (b > 10) ? c : d;

読みにくく混乱するので、3項演算子をチェーンまたはネストしないでください

int a = b > 10 ? c < 20 ? 50 : 80 : e == 2 ? 4 : 8;

さらに、三項演算子を使用する場合は、読みやすさを向上させる方法でコードをフォーマットすることを検討してください。

int a = (b > 10) ? some_value                 
                 : another_value;

80
最初の数文に完全に同意しますが、「読みやすさの向上」の例には完全に同意しません。複数行を使用する場合は、ifステートメントを使用しないでください。
ジョーフィリップス、

3
なぜなら、もしそれ以外の場合、単純な決定のほうがかなり冗長だからです。inta = 0; if(b> 10)a = some_value; それ以外の場合、a = another_value; あなたは何を好むか?
marcospereira、2009年

44
@ d03boy:ifステートメントはそれだけのステートメントなので、必要なのが式だけの場合は実行されません。
falstro、2010

2
一部の言語では@roe(式がScalaの場合など)なのでval x = if(true) 0 else 1、完全に合法です
om-nom-nom

5
@ om-nom-nomの場合、それはifステートメントではなくif式になり、本質的に?:演算子と同じです。
falstro

140

各サブ式にブレークポイントを配置できないため、デバッグが少し難しくなります。めったに使用しません。


44
それは私が今まで聞いた三項演算子に対する最高の議論です。私は「読めない」という議論(人々がそれに慣れるのが面倒すぎるように聞こえます)を購入しませんが、これには実際には実質があります。
EpsilonVector

80

特にタイプセーフな言語では、私はそれらを愛しています。

私はこれがどのように見えるのかわかりません:

int count = (condition) ? 1 : 0;

これより難しいです:

int count;

if (condition)
{
  count = 1;
} 
else
{
  count = 0;
}

編集-

三項演算子は、すべてを代替よりも複雑でなく、よりきちんと整えると私は主張します。


5
変数が定数の場合、DまたはC ++では3値の初期化がさらに役立ちます。例 const int count = ...;
deft_code

さて、あなたはif/elseそこにある不必要なブレースを誤って伝えているようです。
bobobobo 2012

1
また、このケースであればconditionあるbool あなただけ行うことができます int count = condition;
bobobobo

1
@boboboboこのif/else中括弧では、プログラマの大半は三元を書き換えする方法..です
アンドレ・フィゲイレド

1
@bobobobo if / else w / o中括弧は問題を求めているだけです。これは、(ブロックの一部として追加の行を実行)の行を追加しますが、それは、彼らが期待するものを行うために括弧が必要になりますことを忘れて誰かのためにあまりにも簡単です。stackoverflow.com/a/381274/377225
ジョージ・マリアン

43

連鎖私は大丈夫です-入れ子になっている、それほどではありません。

私はそれらをCでより多く使用する傾向がありますb / cそれらは値を持つifステートメントなので、不要な繰り返しや変数を削減します。

x = (y < 100) ? "dog" :
    (y < 150) ? "cat" :
    (y < 300) ? "bar" : "baz";

のではなく

     if (y < 100) { x = "dog"; } 
else if (y < 150) { x = "cat"; }
else if (y < 300) { x = "bar"; } 
else              { x = "baz"; }

このような割り当てでは、リファクタリングが少なく、明確です。

一方、ルビーで作業しているときはif...else...end、表現でもあるので、使用する可能性が高くなります。

x =   if (y < 100) then "dog"
    elif (y < 150) then "cat"
    elif (y < 300) then "bar"
    else                "baz"
    end

(確かに、これほど単純なもののために、とにかく私は三項演算子を使用するかもしれません)。


2
私はあなたの最初の例が好きです-私は以前にそのようにそれらを連鎖することを考えていませんでした。共有いただきありがとうございます。=)
Erik Forbes

5
+1私は実際に最近それを始めました。実際には、switchステートメントを置き換えるためにもそれを行います。
Davy8

1
@rampionの優れた例。
イルミネート2013年

39

Ternary ?:演算子は、手続き型のif構造と機能的に同等です。したがって、ネストされた?:式を使用していない限り、任意の操作の関数表現に対する/に対する関数表現の引数がここに適用されます。ただし、3項演算をネストすると、コードがまったく混乱する可能性があります(読者のための練習:ネストされた3項条件を処理するパーサーを作成してみてください。その複雑さを理解してください)。

ただし、?:演算子を慎重に使用すると、コードが他の方法よりも実際に読みやすくなる状況がたくさんあります。例えば:

int compareTo(Object object) {
    if((isLessThan(object) && reverseOrder) || (isGreaterThan(object) && !reverseOrder)) {
       return 1;
    if((isLessThan(object) && !reverseOrder) || (isGreaterThan(object) && reverseOrder)) {
       return -1;
    else
      return 0;              
}

これとこれを比較してください:

int compareTo(Object object) {
    if(isLessThan(object))
        return reverseOrder ? 1 : -1;         
    else(isGreaterThan(object))
        return reverseOrder ? -1 : 1;
    else        
       return 0;              
}

コードがコンパクトになるほど、構文上のノイズが少なくなり、3項演算子を慎重に使用することで(つまり、reverseOrderプロパティにのみ関連して)、最終結果は特に簡潔ではありません。


私はまだ三進法ではないすべてのif / then / else構成で賞賛を使用することを推奨します。そのため、2番目の例にはいくつかのimhoがありません。
クリス

はい、機能します。これは、単一のブール引数を持ち、必要な任意の型を返す小さな関数のようなものです!実際にはきちんとした演算子です。
bobobobo

24

それは本当にスタイルの問題です。私が従う傾向がある潜在意識のルールは次のとおりです。

  • 1つの式のみを評価foo = (bar > baz) ? true : false-so、ただしNOTfoo = (bar > baz && lotto && someArray.Contains(someValue)) ? true : false
  • 表示ロジックに使用している場合、たとえば <%= (foo) ? "Yes" : "No" %>
  • 実際に割り当てにのみ使用してください。決してロジックを制御しない(したがって絶対にしない(foo) ? FooIsTrue(foo) : FooIsALie(foo) 三元系の制御ロジック自体は嘘です。最後の点は無視してください。

シンプルな割り当て操作では簡潔でエレガントなので、気に入っています。


良いガイドラインと良い例!
nickf 08年

ほとんどの言語では制御ロジックに使用できないため、(foo)を実行できませんでしたか?True(foo):False(foo); 割り当てでない限り。
Henry B

おっと、そうだね、私の悪い。
キースウィリアムズ、

21
最初の2つの例は本当に悪いです。比較の結果はすでにブール値であるため、3項演算子は役に立たず、コードを複雑にするだけです。
Trillian、

1
@Trillian +1はい、別の割り当てを行っている必要があります。 foo = (bar > baz);はるかに簡単です
エリック

18

非常に多くの意見の質問のように、答えは必然的です:それは依存します

次のような場合:

return x ? "Yes" : "No";

私はそれよりもはるかに簡潔です(そして私が解析するのが速い):

if (x) {
    return "Yes";
} else {
    return "No";
}

条件式が複雑な場合、三項演算は適切な選択ではありません。何かのようなもの:

x && y && z >= 10 && s.Length == 0 || !foo

三項演算子の良い候補ではありません。

余談ですが、Cプログラマーの場合、GCCには実際に、次のように3項のif-true部分を除外できる拡張機能があります。

/* 'y' is a char * */
const char *x = y ? : "Not set";

どちらが仮定に設定さxれるかはありません。いい物。yyNULL


わずかな構文と文法の確率を修正したSean :-)コードの最後のビットからyが欠落し、「xをyに割り当てる」は「y = x」を意味するため、「xをyに設定」に変更します。
paxdiablo 2009

@パックス:ありがとう!GCC拡張を使用すると、3項のif-true部分は必要ないことを指摘しようとしたので、構文の変更をロールバックしました。
Sean Bright、

その段落は表示されませんでした。ISO標準のコンパイラーではコンパイルできないコードを書くことができるので、私がその種のものに同意することを知らないでください。それでも、GCCが最後に立った人である場合、それは問題になりません:-)
paxdiablo 2009

確かにブードゥー教です...そして、誰がGCCを使用していませんか?:D
ショーンブライト

14

私の考えでは、式が必要な場合にのみ三項演算子を使用することが理にかなっています。

他の場合では、三項演算子は明快さを減少させるようです。


問題は言語の99%であり、式を関数に置き換えることができます。そして、pplが三項演算子を回避すると、その解決策も優先されます。
PierreBdR 2008年

11

尺度によって循環的複雑度の使用if声明や三項演算子は等価です。したがって、その測定では、答えは「いいえ」です。複雑さは以前とまったく同じです。

可読性、保守性、およびDRY(Don't-Repeat-Yourself)などの他の方法により、どちらを選択しても他の方法よりも優れている場合があります。


10

オプションのパラメーターがnullの場合にデフォルト値を定義するために、コンストラクター(たとえば、新しい.NET 3.5 LINQ to XMLコンストラクト)での作業が制限されている場所でよく使用します。

考案された例:

var e = new XElement("Something",
    param == null ? new XElement("Value", "Default")
                  : new XElement("Value", param.ToString())
);

または(アステライトに感謝)

var e = new XElement("Something",
    new XElement("Value",
        param == null ? "Default"
                      : param.ToString()
    )
);

三項演算子を使用するかどうかに関係なく、コードが読み取り可能であることを確認することが重要です。どんな構造も読めなくすることができます。


または... var e = new XElement( "Something"、new XElement( "value"、param == null? "Default":param.toString()));
アステライト

2
読みやすくするためにフォーマットしているのが好きなので、多くはフォーマットしていません。
bruceatk 2008年

人間が読めるものではない場合、人間が読めるソースコードはどのように役立ちますか?=)
Erik Forbes

9

コードを非常に読みにくくしない限り、可能な限り3項演算子を使用しますが、それは通常、コードが少しのリファクタリングを使用できることを示しているだけです。

一部の人々が三項演算子が「隠された」機能であるか、またはいくぶん神秘的であると考える人がいかにあるかについて、私は常に困惑します。これは、Cでプログラミングを始めたときに最初に学んだことの1つであり、読みやすさを低下させることはないと思います。それは言語の自然な部分です。


1
私は完全に同意しています。それについて隠されていたりトリッキーなことは何もありません。
mmattax 2008年

2
特にネストされている場合、読みやすさの問題を引き起こす可能性があります。
David Thornley、

非常に読みにくい」というのは少し寛大すぎると思いますが、全体的にはあなたの意見に同意します。難しいことや神秘的なことは何もありません。
EpsilonVector

8

(その日のハック)

#define IF(x) x ?
#define ELSE :

次に、式としてif-then-elseを実行できます。

int b = IF(condition1)    res1
        ELSE IF(condition2)  res2
        ELSE IF(conditions3) res3
        ELSE res4;

7

私はjmulderに同意します。これはの代わりに使用するべきではありませんがif、return式または式の内部で使用できます。

echo "Result: " + n + " meter" + (n != 1 ? "s" : "");
return a == null ? "null" : a;

前者は単なる例であり、複数形のより優れたi18nサポートを使用する必要があります。


最初の3項は正しくありません-を持っています:行く必要があります:(n!= 1 "s": "")
Erik Forbes

はい、指摘してくれてありがとう!修繕。
PhiLho 2008年

6

単純な条件付き代入に三項演算子を使用している場合は、問題ないと思います。割り当てさえせずにプログラムフローを制御するために使用されることを(ab)は見てきましたが、それは避けるべきだと思います。このような場合は、ifステートメントを使用してください。


5

必要なときに三項演算子を使用する必要があると思います。これは明らかに非常に主観的な選択ですが、単純な式(特に戻り式として)は完全なテストよりもはるかに明確であることがわかりました。C / C ++での例:

return (a>0)?a:0;

に比べ:

if(a>0) return a;
else return 0;

三項演算子と関数の作成の間に解決策がある場合もあります。たとえばPythonでは:

l = [ i if i > 0 else 0 for i in lst ]

別の方法は次のとおりです。

def cap(value):
    if value > 0:
        return value
    return 0
l = [ cap(i) for i in lst ]

Pythonでは(例として)、そのようなイディオムが定期的に見られるほど十分に必要です。

l = [ ((i>0 and [i]) or [0])[0] for i in lst ]

この行は、Pythonの論理演算子のプロパティを使用しています。これらは遅延であり、最終状態と等しい場合に計算された最後の値を返します。


3
その最後の行は私の脳を傷つけます...
エリックフォーブス

5

私はそれらが好きです。理由はわかりませんが、3進法を使うととてもクールに感じます。


5

私はそのような獣を見てきました(isValidDateであり、月と日もチェックしたため、実際にははるかに悪かったのですが、すべてを覚えようとすることに煩わされませんでした)。

isLeapYear =
    ((yyyy % 400) == 0)
    ? 1
    : ((yyyy % 100) == 0)
        ? 0
        : ((yyyy % 4) == 0)
            ? 1
            : 0;

明らかに、一連のifステートメントの方が優れていたはずです(ただし、これは私がかつて見たマクロバージョンよりも優れています)。

次のような小さなことについては気にしません:

reportedAge = (isFemale && (Age >= 21)) ? 21 + (Age - 21) / 3 : Age;

または次のような少しトリッキーなもの:

printf ("Deleted %d file%s\n", n, (n == 1) ? "" : "s");

ステートメントを読みやすくするために、一連の分離をどのように言えますか?私はあなたの「ビースト」をうまく読みました。reportedAgeいくつかの計算が必要な例です-おそらくそれが理解するよりも特定のケースの方が多いためですisThisYearALeapYear
Axeman

4

まあ、その構文は恐ろしいです。機能的なifは非常に便利で、コードを読みやすくします。

私はそれをより読みやすくするためにマクロを作ることを提案しますが、誰かが恐ろしいエッジケースを思い付くことができると確信しています(CPPには常にあるので)。


多くのBASIC実装およびバリアントには、3項演算子の代わりとなるIF関数があります。私はCのマクロとして定義されるものとコードベースの数見た
SPARR

まあ、私は関数型プログラミング言語を考えていましたが、そうです。
Marcin、

「マクロを読みやすくするために作る」というのは冗談です。
niXar 2009年

4

デバッグコードで演算子を使用してエラー値を出力するのが好きなので、常に調べる必要はありません。通常、これは、開発が終了すると残っていないデバッグプリントに対して行います。

int result = do_something();
if( result != 0 )
{
  debug_printf("Error while doing something, code %x (%s)\n", result,
                result == 7 ? "ERROR_YES" :
                result == 8 ? "ERROR_NO" :
                result == 9 ? "ERROR_FILE_NOT_FOUND" :
                "Unknown");
}


4

三項演算子を使用するときはいつでも、それを維持しようとするときよりもずっと考える必要があるため、私はほとんど使用しません。

冗長性を避けたいのですが、コードの選択がはるかに簡単になったら、冗長性を追求します。

考慮してください:

String name = firstName;

if (middleName != null) {
    name += " " + middleName;
}

name += " " + lastName;

さて、それは少し冗長ですが、私はそれよりもはるかに読みやすいと思います:

String name = firstName + (middleName == null ? "" : " " + middleName)
    + " " + lastName;

または:

String name = firstName;
name += (middleName == null ? "" : " " + middleName);
name += " " + lastName;

何が起こっているのか明確にせずに、あまりにも多くの情報を少なすぎるスペースに圧縮しているように見えます。3項演算子の使用を目にするたびに、ずっと読みやすい代替案を常に見つけてきました...繰り返しになりますが、これは非常に主観的な意見なので、あなたや同僚が3項演算子を非常に読みやすいと感じたら、それを試してください。


1
それはまったく同じことではありません。2番目の例では、3つのステートメントすべてを1行に圧縮しています。これは、三項演算子ではなく、読みやすさを低下させるものです。
非合法08年

十分に公平で、あなたのコメントを組み込むように更新しましたが、それでも私には煩雑に感じられます...しかし、再び、それは主観的です...私は三元が読めないと言っていません、私には読めないと言っています(99時間の割合)
マイクストーン


3

私は通常、次のようなもので使用します。

before:

if(isheader)
    drawtext(x,y,WHITE,string);
else
    drawtext(x,y,BLUE,string);

after:

    drawtext(x,y,isheader==true?WHITE:BLUE,string);

もちろん、ほとんどの言語では、その3進数の「== true」の部分も必要ありません。
マイケルハーレン、

コンパイラーはコードを読みやすくするためだけに挿入する傾向があるものの、コンパイラーは== trueがない場合と同じように最適化する必要がある
KPexEA

いいえ言語あなたは、おそらく「== true」を必要とすることができます
niXar

私は賛成するか否かを決めるのに苦労しました。例はいいですが、== TRUEは他の人のコードで見るのが我慢できないものです。
PeterPerháč10年

3

他の人が指摘したように、それらは短い単純な条件に適しています。特に彼らのようなI(一種のようなのデフォルト設定については||及びあるいは、例えばJavaScriptとのpythonでの使用)

int repCount = pRepCountIn ? *pRepCountIn : defaultRepCount;

もう1つの一般的な用途は、C ++で参照を初期化することです。参照は同じステートメントで宣言および初期化する必要があるため、ifステートメントは使用できません。

SomeType& ref = pInput ? *pInput : somethingElse;

2
これは参照の初期化に関する最初の言及であることは驚くべきことです。これは、「:」の代わりに「if」を使用できない数少ない場所の1つです。(これはC ++固有の質問ではないためだと思います...)同じ理由で、コンストラクタの初期化リストでも役立ちます。
j_random_hacker 2009

2

私は三項演算子をGOTOのように扱います。それらは場所がありますが、コードを理解しやすくするために通常は避けるべきものです。


2

私は最近、標準の "()?:"バリアントをわかりやすくするための3項演算子(そうですね)のバリエーションを見ました。

var Result = [CaseIfFalse, CaseIfTrue][(boolean expression)]

または、より具体的な例を示すには:

var Name = ['Jane', 'John'][Gender == 'm'];

ちなみに、これはJavascriptなので、他の言語では(ありがたいことに)そのようなことはできないかもしれません。


1
うわー、それはひどいです!それらのいくつかを一緒にネストすることを想像してください!2つの要素の配列を返す関数があった場合、それがぼんやりと見えるだけでわかります。varName = getNames()[Gender == 'm']; ...しかし、それはあまり読めません!
nickf 2008年

2

単純なifの場合、私はそれを使用するのが好きです。実際には、たとえば、関数やそのようなもののパラメーターとして、読み取り/コード化する方がはるかに簡単です。また、新しい行を回避するために、すべてのif / elseを使い続けます。

それを否定することは、私の本の中で大きなNO-NOでしょう。

したがって、再開して、単一のif / elseの場合は、3項演算子を使用します。その他の場合は、通常のif / else if / else(またはスイッチ)


2

Elov演算子と呼ばれるGroovyの三項演算子の特殊なケースが好きです:?:

expr ?: default

このコードは、nullでない場合はexprに評価され、nullの場合はデフォルトに評価されます。技術的には、それは実際には三項演算子ではありませんが、それは間違いなくそれに関連しており、多くの時間/タイピングを節約します。


うん、私もその1を愛する-それはだ??C#、ヌル合体演算子で:stackoverflow.com/questions/278703/...
ジャロッド・ディクソン

2

条件に応じて異なる値を割り当てるなどの単純なタスクには最適です。条件によってはもっと長い表現があるときは使いません。


2

非常に多くの答えが言っている、それは依存します。コードのクイックスキャンで3項比較が表示されない場合は、使用しないでください。

副次的な問題として、Cでは比較テストがステートメントであるという事実により、その存在自体が実際には少し異常であることに注意するかもしれません。Iconでは、if(ほとんどのIconと同様に)構成は実際には式です。したがって、次のようなことができます。

x[if y > 5 then 5 else y] := "Y" 

...三項比較演算子よりもずっと読みやすいと思います。:-)

最近、?:アイコンに演算子を追加する可能性についての議論がありましたが、いくつかの人々は、動作方法のために絶対に必要がないことを正しく指摘しましたif

つまり、C(または三項演算子を持つ他の言語)でそれを実行できれば、実際には三項演算子はまったく必要ありません。


2

あなたとあなたの同僚が彼らが何をしているのかを理解していて、彼らが大規模なグループで作成されていない場合、私は彼らが単にコードが少ないので、コードをそれほど複雑でなく読みやすくしていると思います。

三項演算子がコードを理解するのを難しくすると思うのは、1行に3または4を超える場合だけです。ほとんどの人は、それらが正しいベースの優先順位であることを覚えていません。それらのスタックがあると、コードを読むのが悪夢になります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.