複合AND / OR ifステートメントのスタイル設定


13

読みやすくするために、複雑な複合AND / OR ifステートメントをどのようにスタイルしますか?どのようにインデントし、改行をどこに配置しますか?私の特定の状況は次のようなものです。すべてを1行に分割するよりも間違いなく優れていますが、それでも面倒です。

if (
    (
        x == y
        && a != b
        && p.isGood() 
        && (
            i + u == b
            || q >= a
        )
    )
    || k.isSomething()
    || m > n
) {
    doSomething();
}

1
間抜けなインデントと括弧/ブレース構造は意図的ですか、それともスタイルの一部ですか?
エドS.

おかしい。私は1週間前にSOでこの同じ質問をしましたが、閉じられました。この質問がどこかで生きているのを見てうれしいです!
エリックベルエア

回答:


6

小さなステップごとにブール変数を作成します。

bool step1 = i + u == b || q >= a;
bool step2 = a != b && p.isGood() && group1;
bool step3 = group2 || k.isSomething() || m > n;
if (step3) { doSomething(); }

もちろん、これはLacrymologyの答えに似ていますが、各ステップの名前が異なる点が異なります。

名前場合はstep1step2step3の良好な概念的な意味をなすかの方法で、これはこれまでで最も読みやすいでなければなりません。p.isGood()そしてk.isSomething()それらの機能は高価である場合や、非常にタイトなループでこのコードを実行している場合、このオプションではありませんので、時には、それはあなたの元のコードではないだろう状況で起動することができます。

一方、新しい変数を作成するとパフォーマンスが低下することを心配する必要はありません。優れたコンパイラーはそれらを最適化します。

長方形の衝突検出の例(前述のパフォーマンスヒットのため、おそらく使用しないでしょう):

if((a.x + a.width >= b.x || b.x + b.width >= a.x)
 && (a.y + a.height >= b.y || b.y + b.width >= a.y)
)
{ collision(); }

になるかもしれません:

bool horizMatch = a.x + a.width >= b.x || b.x + b.width >= a.x;
bool vertMatch = a.y + a.height >= b.y || b.y + b.width >= a.y;
if(horizMatch && vertMatch) { collision(); }

また、コードをそのままにしておきたい場合は、それでもまったく問題ないと思います。あなたのコードは非常に読みやすいと正直に思います。明らかに、私は正確にa b x y i u p k m nは何なのかわかりませんが、構造に関しては、私には良さそうです。


8

通常、条件が複雑になった場合は、コードをよりモジュール化してリファクタリングします。


この状況では屈折を避けます。そのような小さな関数を単独でテストするのはばかげているでしょう。また、関数の外側に定義がぶら下がると、コードの見た目が見えにくくなります。
宮坂

リファクタリングの程度に依存します。私は、代数の教科書があなたのコードに投げ込まれたように見える条件付き文字列の代わりに意味のある関数名を持つことはあなたのコードをはるかに自明にすると主張します。
JohnFx

しかし、視覚的には、関数が外側にぶら下がっていて、関数が正確に何をするのかすぐにはわかりません。上にスクロールするまで、一時的にブラックボックスになります。関数内の関数を許可する言語を使用している場合を除き、屈折率に関係なく、読者にとっても作家にとっても非常に便利だとは思いません。また、関数内の関数を許可する言語を使用している場合は、代わりにバインディングや変数を宣言するのと構文がほとんど変わらない可能性があります(例:let x = a > bまたは)let f a b = a > b
宮坂

変数も同様に機能します。リファクタリングも検討したいと思います。
JohnFx

うん、いいよ。
宮坂

8

このレベルの複雑さで、私はもっとこのようなことをしたい

bool doIt = x == y && a != b && p.isGood();
doIt &= ( i + u == b || q >= a);
doIt |= k.isSomething() || m > n;

if(doIt)
{
    doSomething();
}

いですが、読みやすく、コンパイラがリファクタリングする方法を知っていることは確かです。

一方、IFステートメントを記述している状況で自分自身を見た場合、解決策を再考します。私はそれを簡単に行う方法があるか、少なくともその条件の一部を抽象化する方法があるからです(たとえば:x == y && a != b && p.isGood()本当にただ意味しthis->isPolygon()、私はその方法を作ることができます。


4

私は時間の経過とともに垂直方向の配置にあまり夢中になりませんが、マルチライン式の一般的な形式は...

if (   (   (expr1 == expr2)
        || (expr3 == expr4)
        || (expr5 == expr6)
       )
    && (   (expr7 == expr8)
        || (expr9 == expra)
       )
   )
{
  blah;
}

キーポイント...

  • 閉じ括弧は、中括弧と同様に、開いた括弧と垂直に整列します。
  • 1行に収まる部分式は1行に収まり、左に垂直に配置されます。読みやすくするために、これらの単一行部分内の挿入演算子も垂直方向に揃えられています。
  • 閉じ括弧は自然にほぼ空白の行を作成し、視覚的にグループ化するのに役立ちます。

時々、フォーマット+して*このような他の演算子をます。非常に少数の複雑な式は、積和または積積形式(ブールの「和」および「製品」を参照できる)をとるので、おそらく一貫したスタイルが価値があるほど十分に一般的です。

ただし、これには注意してください。インデントを使用して複雑すぎる式を読みやすくするよりも、リファクタリング(式の一部を関数に移動するか、中間部を変数に計算して保存する)を行う方が良い場合がよくあります。

あなたが右カレンを右手に積み重ねることを好むなら、私はそれを嫌いではありませんが、私はそれがそれほど悪くないと思います。ただし、あまりにも遠くに行くと、間違いによって、インデントが括弧の動作を誤って表す可能性があります。

if (   (   (expr1 == expr2)
        || (expr3 == expr4)
        || (expr5 == expr6))

    && (   (expr7 == expr8)
        || (expr9 == expra)))
{
  blah;
}

+1私はあなたのスタイリングが好きです。私の質問に直接答えましたが、宮坂Reが問題の根本を突き止めたと思います。レイの方法を怠るなら、あなたのスタイリングを使います。
ジョジョ

うわーこれは本当にいいです。
宮坂

1

http://www.codinghorror.com/blog/2006/01/flattening-arrow-code.html

JohnFxの回答とLacrymologyの回答に同意します。私は小さな目標を達成する関数のセット(できれば静的)を構築し、それらをスマートな方法で構築します。

それでは、このようなものはどうですか?これは完璧な解決策ではありませんが、機能します。これをさらにクリーンアップする方法がありますが、より具体的な情報が必要です。注:コンパイラーは賢いので、このコードは同じくらい速く実行する必要があります。

// Currently based on members or global vars
// (which is often a bad idea too)
function doSomethingCondirionally()
{
  if (k.isSomething() || m > n)
  {
    doSomething();
    return;
  }

  // Else ... 
  if (x != y) return;
  if (a == b) return;
  if (!p.isGood()) return;

  // Final, positive check
  if (i + u == b || q >= a)
  {
    doSomething();
  }
}

関数からの出口点が1つだけであることが重要な場合(たとえば、関数型言語を使用している場合)、これは最良の選択肢ではなく、利用可能なものでもありません。そうは言っても、これは私がよくやることです。
宮坂

Javascriptのようなインタプリタ言語の場合はどうなりますか?
ジョジョ

@ Rei Miyasaka、私はそれがどれだけ価値があるかは言語に依存します。私はLISPファミリーの言語が好きですが、仕事で使用する必要はありませんでした。他の人の機能をリファクタリングしなければならないが、他のコードには触れない場合(多くの場合現実)、上記のようなことをします。このロジックを一から書き/書き直すことができる場合、私のアプローチは異なりますが、著者がここでやろうとしていることの具体的な例を持たずにそのようなコードを書くことはできません。
ジョブ

1
@Rei Miyasaka、その人は天才かもしれないし、がらくたでいっぱいかもしれない。私はすべてを知っているわけではありませんが、その人の単一出口ポイントの防御を知りたいです。これについての議論はこことSOにあり、私が得た印象は、このアプローチは80年代の学者の間で人気があったかもしれないが、もはや問題ではなく、実際に読みやすさを妨げる可能性があるということでした。もちろん、すべてをLINQ機能スタイルで実行している場合、この問題は発生しません。
ジョブ

2
@Job @Steve明示的な割り当て解除を必要とする言語では、より重要なガイドラインだと思います。明らかにすべての機能に対応しているわけではありませんが、リソースを解放することを忘れないようにするために、初心者プログラマーが抱くことが推奨されているのはおそらく習慣です。
宮坂

1

その価値については、あなたの例が私が書いた複雑な述語によく似ていることに驚いた。複雑な述語は保守性や読みやすさのために最大ではないが、ときどき登場することにも同意します。

この部分が正しいことを強調してみましょう。&& a != b 論理的なコネクタを行末に置かないでください。視覚的に見逃すのは簡単すぎます。行の最後に演算子を決して配置しない別の場所は、そのような演算子を使用する言語での文字列連結です。

これを行う:

String a = b
   + "something"
   + c
   ;

これをしないでください:

String a = b +
   "something" +
   c;

論理コネクタのアサーションをサポートするロジックや研究がありますか、それとも単にあなたの好みが事実として述べられているだけですか?これはさまざまな場所でよく耳にしましたが、理解していませんでした(ヨーダの条件付けとは異なり、有効な[見当違いの場合]理由があります)。
カレブホイット-cjhuitt 11年

@Caleb-数学は何世紀にもわたってそのように組版されてきました。コードをスキミングするときは、各行の左側に注目します。演算子で始まる行は明らかに前の行の継続であり、誤ってインデントされた新しいステートメントではありません。
ケビンクライン

数学演算子の接頭辞も好きです。私はプログラミングのキャリアの後半にそれを実現しました:)
ジョジョ

0

条件式がそれほど複雑な場合、通常は条件に分割する必要があることを示しています。おそらく、1つの句を中間変数に割り当てることができます。おそらく、1つの節をヘルパーメソッドに変えることができます。私は通常、1行にそれほど多くのANDやORを持たないことを好みます。


0

コードを複数のステートメントに分割して、理解しやすくすることができます。しかし、本当の忍者はこのようなことをするでしょう。:-)

if
(
    (
        x == y
    &&
        a != b
    &&
        p.isGood()
    &&
        (
            i + u == b
        ||
            q >= a
        )
    )
||
    k.isSomething()
||
    m > n
)
{
    doSomething();
}

5
私はホワイトスペースのファンですが、これは私の好みのために空白に近い行で過度に埋められています。
Steve314
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.