三元か三元か? [閉まっている]
-
03-07-2019 - |
質問
私は個人的に三項演算子の擁護者です:()? :;私はそれがその場所を持っていることを理解していますが、私はこれを使用することに完全に反対し、それをあまりにも頻繁に使用する多くのプログラマに出会いました。
それに対するあなたの気持ちは?あなたはそれを使用してどのような興味深いコードを見ましたか?
解決
単純な式にのみ使用:
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;
他のヒント
各サブ式にブレークポイントを配置できないため、デバッグが少し難しくなります。めったに使用しません。
特にタイプセーフな言語では、私はそれらが大好きです。
どのように表示されるのかわかりません:
int count = (condition) ? 1 : 0;
これより難しい:
int count;
if (condition)
{
count = 1;
}
else
{
count = 0;
}
編集-
三項演算子を使用すると、すべてのものが他の演算子よりも複雑でなく、簡潔になります。
私は大丈夫です-入れ子になっていますが、それほどではありません。
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
(確かに、この単純なことのために、とにかく三項演算子を使用するかもしれません)
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;
}
コードがよりコンパクトであるため、構文上のノイズが少なくなり、三項演算子を慎重に使用することで( reverseOrder プロパティとの関係でのみ)、最終結果は特に簡潔になりません。
これは本当にスタイルの問題です。私が従う傾向がある潜在意識のルールは次のとおりです。
- 1つの式のみを評価します-
foo = (bar > baz) ? true : false
ですが、foo = (bar > baz && lotto && someArray.Contains(someValue)) ? true : false
ではありません
- 表示ロジックに使用している場合、たとえば
<%= (foo) ? "Yes" : "No" %>
-
割り当てにのみ実際に使用します。絶対にフローロジックを使用しない(したがって(foo) ? FooIsTrue(foo) : FooIsALie(foo)
)ことはありません
単純な割り当て操作には簡潔でエレガントなため、気に入っています。
非常に多くの意見の質問のように、答えは必然的です:それは依存します
次のような場合:
return x ? "Yes" : "No";
それは以下よりずっと簡潔(そして私が解析するのが速い)だと思います
if (x) {
return "Yes";
} else {
return "No";
}
条件式が複雑な場合、3項演算は適切な選択ではありません。次のようなもの:
x && y && z >= 10 && s.Length == 0 || !foo
は三項演算子の良い候補ではありません。
余談ですが、Cプログラマーの場合、GCCには実際には拡張機能を使用すると、次のように三項のif-true部分を除外できます。
/* 'y' is a char * */
const char *x = y ? : "Not set";
は、x
がy
ではないと仮定して、NULL
を<=>に設定します。良いもの。
私の考えでは、式が必要な場合にのみ三項演算子を使用する意味があります。
他のケースでは、三項演算子が明瞭さを低下させるようです。
循環的複雑さの測定により、if
ステートメントまたは三項演算子の使用同等です。したがって、その尺度では、答えはいいえであり、複雑さは以前とまったく同じになります。
読みやすさ、保守性、DRY(Don't-Repeat-Yourself)などの他の手段により、どちらの選択肢が他の選択肢よりも優れている可能性があります。
コンストラクターでの作業が制限されている場所(たとえば、新しい.NET 3.5 LINQ to XMLコンストラクト)で頻繁に使用して、オプションのパラメーターがnullの場合の既定値を定義します。
不自然な例:
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()
)
);
三項演算子を使用するかどうかに関係なく、コードを読みやすくすることは重要です。どの構造も読めなくすることができます。
コードを非常に読みにくくしない限り、可能な限り三項演算子を使用しますが、それは通常、コードが少しリファクタリングを使用できることを示しています。
3項演算子が<!> quot; hidden <!> quot;であると考える人がいるのはいつも戸惑います。機能またはやや神秘的です。これは、Cでプログラミングを始めたときに学んだ最初のことの1つであり、読みやすさを低下させるとは思わない。それは言語の自然な部分です。
jmulderに同意します。if
の代わりに使用すべきではありませんが、return式または式の内部にその場所があります:
echo "Result: " + n + " meter" + (n != 1 ? "s" : "");
return a == null ? "null" : a;
前者は単なる例であり、複数形のより良い国際化サポートを使用する必要があります!
(今日のハック)
#define IF(x) x ?
#define ELSE :
その後、式としてif-then-elseを実行できます:
int b = IF(condition1) res1
ELSE IF(condition2) res2
ELSE IF(conditions3) res3
ELSE res4;
単純な条件付き割り当てに三項演算子を使用している場合は、問題ないと思います。割り当てを行わずにプログラムフローを制御する(ab)のを見てきたので、それは避けるべきだと思います。これらの場合はifステートメントを使用してください。
必要に応じて、三項演算子を使用する必要があると思います。これは明らかに非常に主観的な選択ですが、単純な式(特にリターン式)は完全なテストよりもはるかに明確であることがわかります。 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進表現を使用すると非常にクールに感じます。
私はそのような獣を見てきました(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");
エラー値を出力するためにデバッグコードで演算子を使用するのが好きなので、常にそれらを調べる必要はありません。通常、これは、開発が終了すると残っていないデバッグプリントに対して行います。
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");
}
三項演算子を使用することはほとんどありません。使用するたびに、後で維持しようとするときよりもずっと多くのことを考えるようになるからです。
冗長性を回避したいのですが、コードを簡単に理解できるようになったら、冗長性に取り組みます。
検討:
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;
何が起こっているのかを明確にせずに、あまりにも多くの情報を小さすぎるスペースに圧縮しているようです。三項演算子が使用されるのを見るたびに、ずっと読みやすいと思われる代替案を常に見つけました...それは非常に主観的な意見ですので、あなたとあなたの同僚が三項演算子を非常に読みやすいと思うなら、それを選んでください。
まあ、それの構文は恐ろしいです。機能的ifは非常に便利で、コードを読みやすくすることがよくあります。
より読みやすくするためにマクロを作成することをお勧めしますが、誰かが恐ろしいエッジケースを思い付くことができると確信しています(常にCPPにあります)。
次の場合のみ:
$ var =(simple <!> gt; test?simple_result_1:simple_result_2);
KISS。
私は通常、次のようなもので使用します:
before:
if(isheader)
drawtext(x,y,WHITE,string);
else
drawtext(x,y,BLUE,string);
after:
drawtext(x,y,isheader==true?WHITE:BLUE,string);
他の人が指摘したように、彼らは短い単純な条件に適しています。特にデフォルト(Javascriptおよびpythonでの || やまたはの使用のようなもの)で気に入っています。例:
int repCount = pRepCountIn ? *pRepCountIn : defaultRepCount;
別の一般的な使用法は、C ++で参照を初期化することです。参照は同じステートメントで宣言および初期化する必要があるため、ifステートメントは使用できません。
SomeType& ref = pInput ? *pInput : somethingElse;
私は三項演算子をGOTOのように扱います。それらには場所がありますが、コードを理解しやすくするために通常避けるべきものです。
最近、標準の<!> quot;()を作成する三項演算子(まあ、並べ替え)のバリエーションを見ました。 :<!> quot;バリアントは明快さの象徴のようです:
var Result = [CaseIfFalse, CaseIfTrue][(boolean expression)]
または、より具体的な例を挙げます:
var Name = ['Jane', 'John'][Gender == 'm'];
覚えておいてください、これはJavascriptなので、そのようなことは他の言語では不可能かもしれません(ありがたいことに)。
単純なifケースの場合、使用するのが好きです。実際、関数やそのようなもののパラメーターとして、たとえば読み取り/コード化する方がはるかに簡単です。また、新しい行を避けるために、すべてのif / elseを保持するのが好きです。
それを否定することは、私の本では大きなNO-NOになります。
したがって、再開すると、単一のif / elseに対して三項演算子を使用します。その他の場合は、通常のif / else if / else(またはスイッチ)
エルビス演算子と呼ばれるGroovyの三項演算子の特殊なケースが好きです:?:
expr ?: default
このコードは、nullでない場合はexprに評価され、nullの場合はデフォルトに評価されます。技術的には実際には三項演算子ではありませんが、間違いなくそれに関連しており、多くの時間/入力を節約します。
条件に応じて異なる値を割り当てるような単純なタスクの場合、それらは素晴らしいです。条件thoに応じてより長い式がある場合は使用しません。
非常に多くの回答が言っています、それは依存しています。コードのクイックスキャンで3項比較が表示されない場合は、使用しないでください。
副次的な問題として、Cでの比較テストはステートメントであるという事実のために、その存在自体が実際には少し変則的であることに注意するかもしれません。 Iconでは、if
コンストラクト(ほとんどのIconと同様)は実際には式です。次のようなことができます:
x[if y > 5 then 5 else y] := "Y"
... ternery比較演算子よりもはるかに読みやすいと思います。 :-)
最近、Iconに?:
演算子を追加する可能性について議論がありましたが、<=>が機能するため、まったく必要がないことをいくつかの人々が正しく指摘しました。
これは、C(またはternery演算子を持つ他の言語)でそれができれば、実際にはternery演算子がまったく必要ないことを意味します。
あなたとあなたの同僚が彼らが何をしているのかを理解していて、彼らが大規模なグループで作成されていない場合、単純にコードが少ないため、コードが複雑にならず読みやすくなります。
三項演算子がコードを理解しにくくすると思うのは、1行に3つまたは4つ以上ある場合だけです。ほとんどの人は、それらが正しいベースの優先順位であることを覚えておらず、あなたがそれらのスタックを持っているとき、それはコードを読むことを悪夢にします。