私は個人的に三項演算子の支持者です: () ? : ; 私はそれがその場所であることを認識していますが、それを使用することに完全に反対している多くのプログラマーと、あまりにも頻繁に使用しているプログラマーに出くわしました.
あなたの気持ちはどうですか?それを使用して、どのような興味深いコードを見ましたか?
私は個人的に三項演算子の支持者です: () ? : ; 私はそれがその場所であることを認識していますが、それを使用することに完全に反対している多くのプログラマーと、あまりにも頻繁に使用しているプログラマーに出くわしました.
あなたの気持ちはどうですか?それを使用して、どのような興味深いコードを見ましたか?
単純な式のみに使用します。
int a = (b > 10) ? c : d;
読みにくく混乱を招くため、三項演算子を連鎖またはネストしないでください。
int a = b > 10 ? c < 20 ? 50 : 80 : e == 2 ? 4 : 8;
さらに、三項演算子を使用する場合は、読みやすくなるようにコードをフォーマットすることを検討してください。
int a = (b > 10) ? some_value
: another_value;
サブ式のそれぞれにブレークポイントを配置できないため、デバッグが少し難しくなります。めったに使いません。
Chained 私は大丈夫です - ネストされていますが、それほどではありません。
値を持つifステートメントであるという理由だけで、C でより多く使用する傾向があるため、不要な繰り返しや変数が削減されます。
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"; }
このような割り当てでは、リファクタリングが少なくなり、より明確になることがわかりました。
一方、Ruby で作業しているときは、それif...else...end
も式なので使用する可能性が高くなります。
x = if (y < 100) then "dog"
elif (y < 150) then "cat"
elif (y < 300) then "bar"
else "baz"
end
(ただし、確かに、これほど単純なものについては、とにかく三項演算子を使用するだけかもしれません。)
三項 ?:
演算子は、手続き型if
構造と機能的に同等です。ネストされた式を使用していない限り、?:
操作の機能表現に対する/に対する引数がここに適用されます。ただし、三項演算をネストすると、コードがまったく混乱する可能性があります(読者の練習:ネストされた三項条件を処理するパーサーを作成してみてください。その複雑さに感謝します)。
ただし、演算子を保守的に使用すると、実際には他の方法よりも読みやすい?:
コードになる可能性がある状況はたくさんあります。例えば:
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;
}
コードがよりコンパクトであるため、構文上のノイズが少なくなり、三項演算子を慎重に使用することで(つまり、reverseOrderプロパティに関連する場合のみ)、最終結果は特に簡潔ではありません。
それは本当にスタイルの問題です。私が従う傾向がある潜在意識のルールは次のとおりです。
foo = (bar > baz) ? true : false
、ただし NOTfoo = (bar > baz && lotto && someArray.Contains(someValue)) ? true : false
<%= (foo) ? "Yes" : "No" %>
(foo) ? FooIsTrue(foo) : FooIsALie(foo)
)単純な代入操作が簡潔でエレガントなので気に入っています。
私の考えでは、式が必要な場合にのみ三項演算子を使用する意味があります。
他の場合では、三項演算子が明瞭さを低下させているように見えます。
コードが非常に読みにくくならない限り、可能な限り三項演算子を使用しますが、それは通常、私のコードが少しリファクタリングを使用できることを示しているにすぎません。
三項演算子が「隠された」機能である、またはやや神秘的であると考える人がいることに、いつも戸惑います。これは、C でプログラミングを始めたときに最初に学んだことの 1 つであり、読みやすさがまったく損なわれるとは思いません。それは言語の自然な部分です。
オプションのパラメーターが 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()
)
);
三項演算子を使用するかどうかに関係なく、コードを読みやすくすることが重要です。任意の構成を読み取り不能にすることができます。
私は jmulder に同意します: a の代わりに使用するべきではありませんがif
、 return 式または式内にその場所があります:
echo "Result: " + n + " meter" + (n != 1 ? "s" : "");
return a == null ? "null" : a;
前者は単なる例であり、より優れた国際化と複数形のローカリゼーション サポートを使用する必要があります。
単純な条件付き割り当てに三項演算子を使用している場合は、問題ないと思います。私はそれが割り当てさえせずにプログラムの流れを制御するために(ab)使用されるのを見てきました、そしてそれは避けるべきだと思います。このような場合は、ifステートメントを使用してください。
(今日のハック)
#define IF(x) x ?
#define ELSE :
次に、式として if-then-else を実行できます。
int b = IF(condition1) res1
ELSE IF(condition2) res2
ELSE IF(conditions3) res3
ELSE res4;
私はそのような野獣を見てきました (それは 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");
必要に応じて三項演算子を使用する必要があると思います。これは明らかに非常に主観的な選択ですが、完全なテストよりも単純な式 (特に戻り式として) の方がはるかに明確であることがわかりました。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 の論理演算子のプロパティを使用します。それらは遅延型であり、最終状態と等しい場合に計算された最後の値を返します。
私は彼らが好き。理由はわかりませんが、三項式を使うととても涼しく感じます。
三項演算子を使用するときはいつでも、後でそれを維持しようとするときに必要以上に考えさせられるため、私はほとんど三項演算子を使用しません。
私は冗長性を避けたいと思っていますが、それによってコードが理解しやすくなる場合は、冗長性を優先します。
検討:
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;
何が起こっているのかを明確にすることなく、あまりにも多くの情報をあまりにも少ないスペースに圧縮しているように見えます. 三項演算子が使用されているのを見るたびに、ずっと読みやすいと思われる代替案を見つけてきました... 繰り返しになりますが、これは非常に主観的な意見です。したがって、あなたや同僚が三項演算子を非常に読みやすいと思う場合は、それを選択してください。
デバッグ コードで演算子を使用してエラー値を出力するのが好きなので、常にエラー値を調べる必要はありません。通常、これは、開発が完了すると残らないデバッグ プリントに対して行います。
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");
}
他の人が指摘しているように、彼らは短い単純な条件に適しています。私は特にデフォルト( ||やJavaScriptやPythonでの使用法のようなもの)でそれらが好きです。
int repCount = pRepCountIn ? *pRepCountIn : defaultRepCount;
もう1つの一般的な使用法は、C++で参照を初期化することです。参照は同じステートメントで宣言および初期化する必要があるため、 ifステートメントを使用することはできません。
SomeType& ref = pInput ? *pInput : somethingElse;
まあ、その構文は恐ろしいです。関数型ifは非常に便利で、多くの場合、コードが読みやすくなります。
読みやすくするためにマクロを作成することをお勧めしますが、誰かが恐ろしいエッジケースを思い付くことができると確信しています (C++ では常にそうです)。
私は三項演算子を GOTO のように扱います。それらには適切な場所がありますが、コードを理解しやすくするために、通常は避けるべきものです。
私は通常、次のようなもので使用します。
before:
if(isheader)
drawtext(x, y, WHITE, string);
else
drawtext(x, y, BLUE, string);
after:
drawtext(x, y, isheader == true ? WHITE : BLUE, string);
多くの答えが言っているように、それは異なります。コードのクイックスキャンで3項比較が表示されない場合は、使用しないでください。
副次的な問題として、Cでは比較テストがステートメントであるという事実のために、その存在自体が実際には少し異常であることに気付くかもしれません。Iconでは、if
(ほとんどのIconのように)構成は実際には式です。したがって、次のようなことができます。
x[if y > 5 then 5 else y] := "Y"
...これは三項比較演算子よりもはるかに読みやすいと思います。:-)
最近、Iconに演算子を追加する可能性についての議論がありましたが、?:
動作の仕方からまったく必要ないという指摘が何人かありましたif
。
つまり、C(または三項演算子を持つ他の言語)でそれを行うことができれば、実際には、三項演算子はまったく必要ありません。
エルビス演算子と呼ばれるGroovyの三項演算子の特殊なケースが好きです:?:
expr ?: default
このコードは、nullでない場合はexprと評価され、nullでない場合はデフォルトと評価されます。技術的には、実際には三項演算子ではありませんが、間違いなくそれに関連しており、多くの時間/入力を節約できます。
簡単な場合は、それを使用するのが好きです。実際には、たとえば関数などのパラメーターとして読み取る/コーディングする方がはるかに簡単です。また、新しい行を避けるために、私はすべてのif/elseを保持するのが好きです。
それを入れ子にすることは、私の本では大したことではありません。
したがって、再開すると、単一のif / elseには、三項演算子を使用します。その他の場合は、通常のif / else if / else(またはスイッチ)。
私は最近、標準の "() ? :" バリアントを明快さのパラゴンのように見せる三項演算子 (まあ、一種の) のバリエーションを見ました。
var Result = [CaseIfFalse, CaseIfTrue][(boolean expression)]
または、より具体的な例を挙げると:
var Name = ['Jane', 'John'][Gender == 'm'];
これは JavaScript なので、他の言語ではそのようなことはできないかもしれません (ありがたいことに)。
いいえ。読みにくいです。If/Elseははるかに読みやすいです。
これは私の意見です。マイレージは異なる場合があります。
論理式の条件の数が多いほど、読みにくくなっていると言えます。これは if 文に当てはまり、三項演算子にも当てはまります。完璧な世界では、他のブランチではなく、 1 つの要約可能な理由が必要です。あなたの説明が「この状態のクラスターが発生したときのみ」である場合、それは実際には「ビジネスルール」に近い可能性があります。
ただし、現実の世界では、理想的なケースに従うためだけに、状態を 1 つの表現可能な状態に折り畳むための中間ステップを追加しません。複数の状態について推論を行ったので、それらの処理方法を決定する必要があります。
if ステートメントで何でもできるので、私は 3 項式が好きです。
if( object.testSomeCondition()) {
System.exec( "format c:" );
}
else {
a++;
}
一方で:
a += ( object.testSomeCondition() ? 0 : 1 );
の値を見つけることが目標であることを明確a
にします。もちろん、それに沿って、合理的な副作用以上のものはおそらくないはずです.
if
より簡単な質問に答えるために、アップストリームで条件を作り直す時間があるかどうかを判断した後で、長い条件または複雑な条件にを使用します。しかし、if を使用すると、別の条件で並列処理を実行しようとします。
if ( user.hasRepeatedlyPressedOKWithoutAnswer()
&& me.gettingTowardMyLunchtime( time )
) {
...
}
また、私の目標は、単一ストリーム処理に近いことです。そのため、私はしばしばを行わないようにしていますが、else
それif
は単に一般的な道から一歩外れているだけです。多くの単一ストリーム処理を行うと、バグがコードに隠れて、飛び出して物事を壊す 1 つの条件を待つのがはるかに難しくなります。
上で述べたように、3 進数を使用して1 つのものを設定する場合、または値を設定するためにテストするケースが少ない場合は、3 進数の読みやすさが気に入っています。
注意点が 1 つあります -->複雑な真の条項はありません
a = b == c ? ( c == d ? ( c == e ? f : g ) : h ) : i;
もちろん、それは次のように分解できます。
a = b != c ? i
: c != d ? h
: c == e ? f
: g
;
そして、それは (圧縮された) 真理値表のように見えます。
読みやすさには、他にも重要な要素があることに注意してください。1 つはブロック長で、もう 1 つはインデント レベルです。三項で単純なことを行うことは、インデントのレベルをさらに進める原動力にはなりません。
三項演算子を使用する必要があるかどうかを判断するために私が最近策定した経験則は次のとおりです。
そして、あなたのコードの読者に親切にしてください。三項演算子をネストしている場合は、そのネストが明確になるようにコードをフォーマットします。
三項演算子は言います。適切にフォーマットすれば、複雑ではありません。paxdiabloのうるう年の例を見てみましょう:
$isLeapYear =
(($year % 400) == 0)
? 1
: ((($year % 100) == 0)
? 0
: ((($year % 4) == 0)
? 1
: 0));
これはより簡潔に書くことができ、このフォーマットでより読みやすくすることができます:
//--------------Test expression-----Result
$isLeapYear = (($year % 400) == 0) ? 1 :
((($year % 100) == 0)? 0 :
((($year % 4) == 0) ? 1 :
0)); // Default result
三項演算子は、コンマ区切りのリストを簡潔に作成するのに非常に便利です。Javaの例を次に示します。
int[] iArr = {1, 2, 3};
StringBuilder sb = new StringBuilder();
for (int i = 0; i < iArr.length; i++) {
sb.append(i == 0 ? iArr[i] : ", " + iArr[i]);
}
System.out.println(sb.toString());
「1、2、3」を生成します
そうしないと、最後のコンマの特殊な大文字小文字が煩わしくなります。
コードの行数を減らしたい場合、またはコードをリファクタリングしている場合は、それを実行してください。
式を理解するためにさらに0.1ミリ秒かかる必要がある次のプログラマーが気になる場合は、とにかくそれを実行してください。
それは本当に彼らが使われている文脈に依存すると思います。
このようなものは、効果的ではありますが、本当に紛らわしい方法です。
__CRT_INLINE int __cdecl getchar (void)
{
return (--stdin->_cnt >= 0)
? (int) (unsigned char) *stdin->_ptr++
: _filbuf (stdin);
}
ただし、これは次のとおりです。
c = a > b ? a : b;
完全に合理的です。
個人的には、過度に冗長なIFステートメントを削減するときに使用する必要があると思います。問題は、人々がそれらを石化しているか、IFステートメントの代わりにほとんど独占的に使用されていることです。
少なくともDのような言語で、型推論をサポートする三項演算子を使用することについて誰も言及していないように思われる理由の1つは、驚くほど複雑なテンプレート型に対して型推論を機能させるためです。
auto myVariable = fun();
// typeof(myVariable) == Foo!(Bar, Baz, Waldo!(Stuff, OtherStuff)).
// Now I want to declare a variable and assign a value depending on some
// conditional to it.
auto myOtherVariable = (someCondition) ? fun() : gun();
// If I didn't use the ternary I'd have to do:
Foo!(Bar, Baz, Waldo!(Stuff, OtherStuff)) myLastVariable; // Ugly.
if(someCondition) {
myLastVariable = fun();
} else {
myLastVariable = gun():
}
ロジックが些細な状況でコード行を避けるために、私は 3 項を使用し、推奨します。
int i;
if( piVal ) {
i = *piVal;
} else {
i = *piDefVal;
}
上記の場合、ノイズが少ないため、3 値を選択します。
int i = ( piVal ) ? *piVal : *piDefVal;
同様に、条件付きの戻り値は良い候補です:
return ( piVal ) ? *piVal : *piDefVal;
コンパクトにすることで可読性が向上し、コードの品質が向上すると思います。
しかし、可読性は常にコードの対象者に依存します。
a ? b : c
読者は頭を働かなくてもパターンを理解できなければなりません。これを推測できない場合は、長いバージョンを使用してください。
コードを小さくしても、解析が容易になるとは限りません。それは言語によって異なります。
たとえば、 PHP では、PHP のレクサーが最初に改行、次に空白で始まるビットにコードを分割するため、空白と改行が推奨されます。したがって、使用する空白が減らない限り、パフォーマンスの問題は見られません。
悪い:
($var)?1:0;
良い:
($var) ? 1 : 0;
大きな問題には思えませんが、PHP のコードを字句解析する場合、空白は不可欠です。さらに、この方法で少し読みやすくなります。
興味深い逸話: インライン化の目的で、オプティマイザが同等のifよりも「重い」ものとして三項演算子を重み付けするのを見たことがあります。Microsoft コンパイラでこれに気付きましたが、もっと広まっている可能性があります。
特に、このような関数はインライン化されます:
int getSomething()
{
return m_t ? m_t->v : 0;
}
しかし、これはそうではありません:
int getSomething()
{
if( m_t )
return m_t->v;
return 0;
}
三項演算子が画面幅全体を占めるようになった場合、私はそれを使用しません。1 つの単純な条件をチェックして、単一の値を返すようにしています。
int x = something == somethingElse ? 0 : -1;
実際には、本番環境で次のような厄介なコードがいくつかあります...良くありません:
int x = something == (someValue == someOtherVal ? string.Empty : "Blah blah") ? (a == b ? 1 : 2 ): (c == d ? 3 : 4);
状況によっては演算子が好きですが、一部の人は使いすぎる傾向があり、コードが読みにくくなる可能性があると思います.
私は最近、変更しようとしているいくつかのオープン ソース コードでこの行に出くわしました。
どこ
(active == null ? true :
((bool)active ? p.active : !p.active)) &&...
それ以外の
where ( active == null || p.active == active) &&...
この場合、三項使用によってLINQステートメントに余分なオーバーヘッドが追加されるのではないかと思います。
それは大好きです。使用するときは、if-then-else のように、条件、true アクション、false アクションを 1 行ずつ記述します。そうすれば、簡単にネストできます。
例:
x = (a == b ? (平方根(a) - 2) : (a*a + b*b) ); x = (a == b ? (平方根(a) - 2) : (a*a + b*b) ); x = (a == b ? (c > d ? (平方根(a) - 2) : (c + cos(d)) ) : (a*a + b*b) );
私にとって、これはかなり読みやすいです。また、サブケースの追加や既存のケースの変更も容易になります。
文字列 someSay = bCanReadThis ? "いいえはい";
少量で行数を減らし、コードを読みやすくすることができます。特に、結果が、計算結果に基づいて文字列を「はい」または「いいえ」に設定するようなものである場合。
例:
char* c = NULL;
if(x) {
c = "true";
}else {
c = "false";
}
と比べて:
char* c = x ? "Yes" : "No";
このような単純なテストで発生する可能性がある唯一のバグは、誤った値を割り当てることですが、条件は通常単純であるため、プログラマーがそれを間違える可能性は低くなります。プログラムが間違った出力を出力することは世界の終わりではなく、コード レビュー、ベンチ テスト、および製品テストのすべてのフェーズで検出する必要があります。
テスト ケースの良さを知るためにコード カバレッジ メトリクスを使用することはますます難しくなっています。最初の例では、両方の割り当て行でカバレッジをテストできます。1 つがカバーされていない場合、テストは考えられるすべてのコード フローを実行していません。
2 番目の例では、行は X の値に関係なく実行されていると表示されるため、代替パス (カバレッジ ツールの能力によってはYMMV )をテストしたとは言えません。
これは、テストの複雑さが増すにつれて、より重要になります。
ここにいる多くのポスターの感情に同意します。三項演算子は、正しく使用され、あいまいさを導入しない限り、完全に有効です (公平を期すために、任意の演算子/構造について言えます)。
私はコードの動作を明確にするために、組み込みコードで三項演算子をよく使用します。次のコード サンプル (わかりやすくするために簡略化しています) をご覧ください。
スニペット 1:
int direction = read_or_write(io_command);
// Send an I/O
io_command.size = (direction==WRITE) ? (32 * 1024) : (128 * 1024);
io_command.data = &buffer;
dispatch_request(io_command);
スニペット 2:
int direction = read_or_write(io_command);
// Send an I/O
if (direction == WRITE) {
io_command.size = (32 * 1024);
io_command.data = &buffer;
dispatch_request(io_command);
} else {
io_command.size = (128 * 1024);
io_command.data = &buffer;
dispatch_request(io_command);
}
ここでは、入力または出力要求をディスパッチしています。要求が読み取りでも書き込みでもプロセスは同じで、デフォルトの I/O サイズが変わるだけです。最初のサンプルでは、三項演算子を使用して、手順が同じであることとsize
、I/O 方向に応じてフィールドが異なる値を取得することを明確にしています。2 番目の例では、2 つのケースのアルゴリズムが同じであることがすぐにはわかりません (特に、コードが 3 行をはるかに超えているため)。2 番目の例では、共通コードの同期を維持することがより困難になります。ここで、三項演算子は、コードの大部分が並列であるという性質をより適切に表現しています。
三項演算子には別の利点があります (ただし、通常は組み込みソフトウェアでのみ問題になります)。一部のコンパイラは、コードが特定の深さを超えて「ネスト」されていない場合にのみ、特定の最適化を実行できます (つまり、関数内では、if、ループ、または switch ステートメントを入力するたびにネストの深さを 1 増やし、次の場合は 1 減らします)。あなたはそれを残します)。場合によっては、三項演算子を使用すると、条件内に含める必要があるコードの量を最小限に抑えることができ (場合によっては、コンパイラーが条件を最適化して除外できるようになります)、コードのネストの深さを減らすことができます。場合によっては、(上記の例のように) 三項演算子を使用して一部のロジックを再構築し、コンパイラが追加の最適化ステップを実行できるように、関数のネストの深さを十分に減らすことができました。
三項演算子なしで難読化されたコードのコンテストに勝つにはどうすればよいでしょうか?!
個人的には、必要に応じて使用することに賛成ですが、ネストすることはないと思います。これは非常に便利ですが、コードが読みにくくなり、他の言語で他の操作 ( Groovyの null チェックなど) で使用されているという点で、いくつかの欠点があります。
私はそれの大ファンです...適切な場合.
このようなものは素晴らしいです。個人的には、読む/理解するのが難しいとは思いません:
$y = ($x == "a" ? "apple"
: ($x == "b" ? "banana"
: ($x == "c" ? "carrot"
: "default")));
ただし、おそらく多くの人がうんざりしていることはわかっています。
PHP で使用する際に留意すべきことの 1 つは、参照を返す関数での動作です。
class Foo {
var $bar;
function Foo() {
$this->bar = "original value";
}
function &tern() {
return true ? $this->bar : false;
}
function ¬Tern() {
if (true) return $this->bar;
else return false;
}
}
$f = new Foo();
$b =& $f->notTern();
$b = "changed";
echo $f->bar; // "changed"
$f2 = new Foo();
$b2 =& $f->tern();
$b2 = "changed";
echo $f2->bar; // "original value"