場合によっては、if
ステートメントはかなり複雑または長いことがあるため、読みやすくするために、if
の前に複雑な呼び出しを抽出することをお勧めします。
例えばこの:
if (SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall())
{
// do stuff
}
これに
bool b1 = SomeComplicatedFunctionCall();
bool b2 = OtherComplicatedFunctionCall();
if (b1 || b2)
{
//do stuff
}
(提供されている例はthat badではなく、単に説明のためです...複数の引数を持つ他の呼び出しを想像してください。)
しかし、この抽出では、短絡評価(SCE)を失いました。
1つの自然な解決策は次のようになります。
bool b1 = SomeCondition();
bool b2 = b1 || SomeOtherCondition();
bool b3 = b2 || SomeThirdCondition();
// any other condition
bool bn = bn_1 || SomeFinalCondition();
if (bn)
{
// do stuff
}
これには、わかりやすく、すべてのケースに適用可能で、短絡動作があるという利点があります。
これが私の最初の解決策でした:メソッド呼び出しとforループ本体の良いパターンは次のとおりです:
if (!SomeComplicatedFunctionCall())
return; // or continue
if (!SomeOtherComplicatedFunctionCall())
return; // or continue
// do stuff
短絡評価と同じ素晴らしいパフォーマンスの利点が得られますが、コードは読みやすくなります。
私は条件を複数の行に分解する傾向があります、すなわち:
if( SomeComplicatedFunctionCall()
|| OtherComplicatedFunctionCall()
) {
複数の演算子(&&)を扱う場合でも、括弧の各ペアでインデントを進める必要があります。 SCEはまだ有効です-変数を使用する必要はありません。この方法でコードを記述すると、すでに何年もずっと読みやすくなりました。より複雑な例:
if( one()
||( two()> 1337
&&( three()== 'foo'
|| four()
)
)
|| five()!= 3.1415
) {
条件の長いチェーンといくつかの短絡を維持するものがある場合、一時変数を使用して複数の条件を組み合わせることができます。あなたの例を挙げると、例えば.
bool b = SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall();
if (b && some_other_expression) { ... }
C++ 11対応のコンパイラがある場合は、上記と同様に lambda式 を使用して式を関数に結合できます。
auto e = []()
{
return SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall();
};
if (e() && some_other_expression) { ... }
以下も使用できます。
bool b = someComplicatedStuff();
b = b || otherComplicatedStuff(); // it has to be: b = b || ...; b |= ...; is bitwise OR and SCE is not working then
sCEが機能します。
しかし、例よりもはるかに読みやすいものではありません。
if (
someComplicatedStuff()
||
otherComplicatedStuff()
)
1)はい、SCEはもうありません。そうでなければ、あなたはそれを持っているでしょう
bool b1 = SomeComplicatedFunctionCall();
bool b2 = OtherComplicatedFunctionCall();
後でif
ステートメントがあるかどうかに応じて、何らかの方法で機能します。複雑すぎます。
2)これは意見に基づいていますが、合理的に複雑な表現では次のことができます。
if (SomeComplicatedFunctionCall()
|| OtherComplicatedFunctionCall()) {
複雑すぎる場合、明らかな解決策は、式を評価して呼び出す関数を作成することです。
1)毎回本当にSCEを失いますか?コンパイラは、「最適化する」ことを許可されたシナリオであり、それでもSCEを提供しますか?
このような最適化は許可されていないと思います。特にOtherComplicatedFunctionCall()
にはいくつかの副作用があるかもしれません。
2)そのような状況でのベストプラクティスは何ですか? (SCEが必要なときに)必要なものすべてをifと "できるだけ読みやすくなるようにフォーマットする"ことだけが可能です。
わかりやすい名前の1つの関数または1つの変数にリファクタリングすることを好みます。これにより、短絡評価と可読性の両方が維持されます。
bool getSomeResult() {
return SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall();
}
...
if (getSomeResult())
{
//do stuff
}
そして、getSomeResult()
とSomeComplicatedFunctionCall()
に基づいてOtherComplicatedFunctionCall()
を実装するとき、それらがまだ複雑な場合は再帰的に分解できます。
1)毎回本当にSCEを失いますか?コンパイラは、「最適化する」ことを許可されたシナリオであり、それでもSCEを提供しますか?
いいえ、そうではありませんが、適用方法が異なります。
if (SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall())
{
// do stuff
}
ここで、OtherComplicatedFunctionCall()
がtrueを返した場合、コンパイラはSomeComplicatedFunctionCall()
を実行しません。
bool b1 = SomeComplicatedFunctionCall();
bool b2 = OtherComplicatedFunctionCall();
if (b1 || b2)
{
//do stuff
}
ここでは、両方の関数willは、b1
およびb2
に格納する必要があるため実行されます。 Ff b1 == true
その後b2
は評価されません(SCE)。ただし、OtherComplicatedFunctionCall()
はすでに実行されています。
b2
が他で使用されていない場合、コンパイラはmight関数に観察可能な副作用がない場合にif内で関数呼び出しをインライン化するのに十分スマートです。
2)そのような状況でのベストプラクティスは何ですか? (SCEが必要な場合)必要なものすべてをifと "できるだけ読みやすくなるようにフォーマットする"ことだけが可能です。
場合によります。 needOtherComplicatedFunctionCall()
副作用のために実行するか、関数のパフォーマンスヒットが最小限である場合、読みやすさのために2番目のアプローチを使用する必要があります。それ以外の場合は、最初のアプローチでSCEに固執します。
短絡し、一箇所に条件がある別の可能性:
bool (* conditions [])()= {&a, &b, ...}; // list of conditions
bool conditionsHold = true;
for(int i= 0; i < sizeOf(conditions); i ++){
if (!conditions[i]()){;
conditionsHold = false;
break;
}
}
//conditionsHold is true if all conditions were met, otherwise false
ループを関数に入れて、関数に条件のリストを受け入れさせ、ブール値を出力させることができます。
非常に奇妙です:コード内のコメントの使用について誰も言及していないとき、あなたは読みやすさについて話している:
if (somecomplicated_function() || // let me explain what this function does
someother_function()) // this function does something else
...
その上で、私は常に関数の前に、関数自体、その入力と出力についていくつかのコメントを付けて、時々例を示します。
/*---------------------------*/
/*! interpolates between values
* @param[in] X_axis : contains X-values
* @param[in] Y_axis : contains Y-values
* @param[in] value : X-value, input to the interpolation process
* @return[out] : the interpolated value
* @example : interpolate([2,0],[3,2],2.4) -> 0.8
*/
int interpolate(std::vector<int>& X_axis, std::vector<int>& Y_axis, int value)
明らかに、コメントに使用するフォーマットは開発環境に依存する場合があります(Visual Studio、EclipseでのJavaDocなど)。
SCEに関する限り、これは次のことを意味していると思います。
bool b1;
b1 = somecomplicated_function(); // let me explain what this function does
bool b2 = false;
if (!b1) { // SCE : if first function call is already true,
// no need to spend resources executing second function.
b2 = someother_function(); // this function does something else
}
if (b1 || b2) {
...
}