29

これは、私が使用したすべての言語で私を悩ませているものです。if ステートメントがありますが、条件部分に非常に多くのチェックがあるため、複数の行に分割するか、ネストされた if ステートメントを使用するか、それが醜いことを受け入れて先に進む必要があります私の人生と。

私や同じ問題に遭遇した他の誰かに役立つ可能性がある、あなたが見つけた他の方法はありますか?

例、すべて 1 行で:

if (var1 = true && var2 = true && var2 = true && var3 = true && var4 = true && var5 = true && var6 = true)
{

例、複数行:

if (var1 = true && var2 = true && var2 = true
 && var3 = true && var4 = true && var5 = true
 && var6 = true)
{

ネストされた例:

if (var1 = true && var2 = true && var2 = true && var3 = true)
{
     if (var4 = true && var5 = true && var6 = true)
     {
4

21 に答える 21

61

条件をいくつかのブール値に分けてから、マスター ブール値を条件として使用します。

bool isOpaque = object.Alpha == 1.0f;
bool isDrawable = object.CanDraw && object.Layer == currentLayer;
bool isHidden = hideList.Find(object);

bool isVisible = isOpaque && isDrawable && ! isHidden;

if(isVisible)
{
    // ...
}

さらに良いことに:

public bool IsVisible {
    get
    {
        bool isOpaque = object.Alpha == 1.0f;
        bool isDrawable = object.CanDraw && object.Layer == currentLayer;
        bool isHidden = hideList.Find(object);

        return isOpaque && isDrawable && ! isHidden;
    }
}

void Draw()
{
     if(IsVisible)
     {
         // ...
     }
}

関数ではなく意図を実際に示す変数名を付けてください。これは、開発者がコードを維持するのに非常に役立ちます...それはあなたかもしれません!

于 2008-08-08T16:54:30.143 に答える
12

まだ誰もこれを手に入れていないことに驚いています。このタイプの問題に特化したリファクタリングがあります。

http://www.refactoring.com/catalog/decomposeConditional.html

于 2008-08-08T17:09:38.477 に答える
7

ここで対処すべき 2 つの問題があります。読みやすさとわかりやすさです。

「読みやすさ」の解決策はスタイルの問題であり、解釈の余地があります。私の好みはこれです:

if (var1 == true && // Explanation of the check
    var2 == true && // Explanation of the check
    var3 == true && // Explanation of the check
    var4 == true && // Explanation of the check
    var5 == true && // Explanation of the check
    var6 == true)   // Explanation of the check
    { }

またはこれ:

if (var1 && // Explanation of the check
    var2 && // Explanation of the check
    var3 && // Explanation of the check
    var4 && // Explanation of the check
    var5 && // Explanation of the check
    var6)   // Explanation of the check
    { }

とはいえ、この種の複雑なチェックは、コードをスキャンしているときに精神的に解析するのが非常に難しい場合があります (特に、オリジナルの作成者でない場合)。複雑さの一部を抽象化するヘルパー メソッドを作成することを検討してください。

/// <Summary>
/// Tests whether all the conditions are appropriately met
/// </Summary>
private bool AreAllConditionsMet (
    bool var1,
    bool var2,
    bool var3,
    bool var4,
    bool var5,
    bool var6)
{
    return (
        var1 && // Explanation of the check
        var2 && // Explanation of the check
        var3 && // Explanation of the check
        var4 && // Explanation of the check
        var5 && // Explanation of the check
        var6);  // Explanation of the check
}

private void SomeMethod()
{
    // Do some stuff (including declare the required variables)
    if (AreAllConditionsMet (var1, var2, var3, var4, var5, var6))
    {
        // Do something
    }
}

「SomeMethod」メソッドを視覚的にスキャンすると、テスト ロジックの実際の複雑さは隠されますが、人間が高レベルで理解できるようにセマンティックな意味は保持されます。開発者が本当に詳細を理解する必要がある場合は、AreAllConditionsMet メソッドを調べることができます。

これは正式には「条件付き分解」リファクタリング パターンとして知られていると思います。Resharper や Refactor Pro などのツール! この種のリファクタリングを簡単に行うことができます!

いずれの場合も、コードを読みやすく理解しやすいものにするための鍵は、現実的な変数名を使用することです。これは不自然な例であることは理解していますが、「var1」、「var2」などは受け入れられない変数名です。それらが表すデータの基本的な性質を反映した名前を付ける必要があります。

于 2008-08-08T17:10:27.897 に答える
6

これらをコンポーネントのブール変数に分割することがよくあります。

bool orderValid = orderDate < DateTime.Now && orderStatus != Status.Canceled;
bool custValid = customerBalance == 0 && customerName != "Mike";
if (orderValid && custValid)
{
...
于 2008-08-08T16:50:57.093 に答える
5

まず、すべての== trueパーツを取り外します。これにより、50% 短くなります ;)

体調が悪いときは原因を探ります。ポリモーフィズムを使用する必要がある場合もあれば、状態オブジェクトを追加する必要がある場合もあります。基本的に、リファクタリングが必要であることを意味します (コードの匂い)。

ブール式を少し単純化するために、 De-Morgan の法則を使用することがあります。

于 2008-08-08T16:49:48.530 に答える
4

Kent Beck による実装パターンを確認してください。この状況で役立つかもしれないと私が考えている特定のパターンがあります...それは「ガード」と呼ばれます。大量の条件を用意するのではなく、それらをガードに分割することができます。これにより、メソッドのどの条件が不利なのかが明確になります。

たとえば、何かを実行するメソッドがあり、そのメソッドが何かを実行してはならない特定の条件がある場合は、次のようになります。

public void doSomething() {
    if (condition1 && condition2 && condition3 && condition4) {
        // do something
    }
}

次のように変更できます。

public void doSomething() {
    if (!condition1) {
        return;
    }

    if (!condition2) {
        return;
    }

    if (!condition3) {
        return;
    }

    if (!condition4) {
        return;
    }

    // do something
}

もう少し冗長ですが、読みやすくなっています。特に、奇妙なネストが発生し始めた場合は、ガードが役立ちます (抽出メソッドと組み合わせると)。

ところで、その本を強くお勧めします。

于 2008-08-08T17:04:33.197 に答える
3

多くの人や編集者が、if ステートメントの各条件を 1 つのタブでインデントしたり、開いた括弧と一致させたりしているのを見てきました。

if (var1 == true
    && var2 == true
    && var3 == true
   ) {
    /* do something.. */
}

私は通常、最後の条件と同じ行に閉じ括弧を置きます。

if (var1 == true
    && var2 == true
    && var3 == true) {
    /* do something.. */
}

しかし、これはそれほどきれいではないと思います。

于 2008-08-08T16:50:23.570 に答える
2

ファンクターと述語を見てみてください。Apache Commonsプロジェクトには、条件付きロジックをオブジェクトにカプセル化できる優れたオブジェクトのセットがあります。それらの使用例は、O'reillyのこちらから入手できます。コード例の抜粋:

import org.apache.commons.collections.ClosureUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.functors.NOPClosure;

Map predicateMap = new HashMap();

predicateMap.put( isHonorRoll, addToHonorRoll );
predicateMap.put( isProblem, flagForAttention );
predicateMap.put( null, ClosureUtils.nopClosure() );

Closure processStudents = 
    ClosureUtils.switchClosure( predicateMap );

CollectionUtils.forAllDo( allStudents, processStudents );

これらすべてのisHonorRoll述語の詳細と、それらを評価するために使用されるクロージャー:

import org.apache.commons.collections.Closure;
import org.apache.commons.collections.Predicate;

// Anonymous Predicate that decides if a student 
// has made the honor roll.
Predicate isHonorRoll = new Predicate() {
  public boolean evaluate(Object object) {
    Student s = (Student) object;

    return( ( s.getGrade().equals( "A" ) ) ||
            ( s.getGrade().equals( "B" ) && 
              s.getAttendance() == PERFECT ) );
  }
};

// Anonymous Predicate that decides if a student
// has a problem.
Predicate isProblem = new Predicate() {
  public boolean evaluate(Object object) {
    Student s = (Student) object;

    return ( ( s.getGrade().equals( "D" ) || 
               s.getGrade().equals( "F" ) ) ||
             s.getStatus() == SUSPENDED );
  }
};

// Anonymous Closure that adds a student to the 
// honor roll
Closure addToHonorRoll = new Closure() {
  public void execute(Object object) {
    Student s = (Student) object;

    // Add an award to student record
    s.addAward( "honor roll", 2005 );
    Database.saveStudent( s );
  }
};

// Anonymous Closure flags a student for attention
Closure flagForAttention = new Closure() {
  public void execute(Object object) {
    Student s = (Student) object;

    // Flag student for special attention
    s.addNote( "talk to student", 2005 );
    s.addNote( "meeting with parents", 2005 );
    Database.saveStudent( s );
  }
};
于 2008-08-18T03:51:12.607 に答える
2

まず最初に、次のことをお勧めします。

if (var1 && var2 && var2 && var3 && var4 && var5 && var6) {
...

また、抽象的なコード例をリファクタリングするのは非常に困難です。特定の例を示すと、問題に適合するより良いパターンを特定しやすくなります。

それは良くありませんが、私が過去に行ったこと: (次の方法はブール値テストの短絡を防ぎます。最初のテストが false であってもすべてのテストが実行されます。常にすべてを実行する必要があることがわかっている場合を除き、推奨されるパターンではありません。戻る前のコード -- 私の間違いを見つけてくれた ptomato に感謝します!)

ブールok = cond1;
わかりました &= cond2;
わかりました &= cond3;
わかりました &= cond4;
わかりました &= cond5;
わかりました &= cond6;

これは次と同じです: (同じではありません。上記の注を参照してください!)

わかりました = (cond1 && cond2 && cond3 && cond4 && cond5 && cond6);

于 2008-08-08T16:55:23.090 に答える
2

Code Completeの Steve Mcconell のアドバイス: 多次元テーブルを使用します。各変数はテーブルへのインデックスとして機能し、if ステートメントはテーブル ルックアップに変わります。たとえば、(サイズ == 3 && 重量 > 70) は、テーブル エントリの決定 [サイズ] [重量グループ] に変換されます。

于 2008-08-18T05:39:28.880 に答える
1

他の人が述べたように、条件を分析して、読みやすさを向上させるために他の方法にアウトソーシングできる方法があるかどうかを確認します。

于 2008-08-08T17:33:46.477 に答える
1

私はブール値を分離することに頼っています:

Bool cond1 == (var1 && var2);
Bool cond2 == (var3 && var4);

if ( cond1 && cond2 ) {}
于 2008-08-08T16:49:54.427 に答える
1

PHP のようなリフレクション言語では、変数-変数を使用できます。

$vars = array('var1', 'var2', ... etc.);
foreach ($vars as $v)
    if ($$v == true) {
        // do something
        break;
    }
于 2010-05-09T12:01:29.030 に答える
0

私がPerlでそれを行っていた場合、これは私がチェックを実行する方法です。

{
  last unless $var1;
  last unless $var2;
  last unless $var3;
  last unless $var4;
  last unless $var5;
  last unless $var6;

  ... # Place Code Here
}

サブルーチンでこれを使用する場合は、のすべてのインスタンスlastreturn;に置き換えてください。

于 2008-10-14T21:07:38.657 に答える
0

私は各条件を記述変数に分割するのが好きです。

bool isVar1Valid, isVar2Valid, isVar3Valid, isVar4Valid;
isVar1Valid = ( var1 == 1 )
isVar2Valid = ( var2.Count >= 2 )
isVar3Valid = ( var3 != null )
isVar4Valid = ( var4 != null && var4.IsEmpty() == false )
if ( isVar1Valid && isVar2Valid && isVar3Valid && isVar4Valid ) {
     //do code
}
于 2008-08-18T04:15:01.220 に答える
0

マクダウェル

式の両側が評価される単一の「&」演算子を使用する場合は正しいです。ただし、'&&' 演算子を使用する場合 (少なくとも C# では)、false を返す最初の式が最後に評価されます。これにより、FOR ステートメントの前に評価を配置することが、他の方法と同じように適切になります。

于 2008-08-08T17:29:52.680 に答える
0

@ツイーク

それは良くありませんが、私が過去にやったこと:

ブールok = cond1; わかりました &= cond2; わかりました &= cond3; わかりました &= cond4; わかりました &= cond5; わかりました &= cond6;

これは次と同じです:

わかりました = (cond1 && cond2 && cond3 && cond4 && cond5 && cond6);

実際、これら 2 つのことはほとんどの言語で同じではありません。2 番目の式は通常、条件の 1 つが false になるとすぐに評価を停止します。条件の評価にコストがかかる場合は、パフォーマンスが大幅に向上する可能性があります。

読みやすさのために、私は個人的に上記の Mike Stone の提案を好みます。詳細にコメントするのは簡単で、早期にアウトできるという計算上の利点をすべて保持します。コードの構成を混乱させて条件付き評価を他の関数から遠くに移動する場合は、同じ手法を関数内でインラインで実行することもできます。少し安っぽいですが、いつでも次のようなことができます。

do {
    if (!cond1)
       break;
    if (!cond2)
       break;
    if (!cond3)
       break;
    ...
    DoSomething();
} while (false);

while (false) は安っぽいです。言語に「once」と呼ばれるスコープ演算子または簡単に抜け出せる何かがあればいいのにと思います。

于 2008-08-08T17:37:12.943 に答える
0

レベルごとに分類するのが好きなので、例を次のようにフォーマットします。

if (var1 = true
 && var2 = true
 && var2 = true
 && var3 = true
 && var4 = true
 && var5 = true
 && var6 = true){

次のように、より多くのネストがある場合に便利です (明らかに、実際の条件はすべての "= true" よりも興味深いでしょう):

if ((var1 = true && var2 = true)
 && ((var2 = true && var3 = true)
  && (var4 = true && var5 = true))
 && (var6 = true)){
于 2008-08-08T16:59:56.747 に答える
0

たまたま Python でプログラミングしている場合は、組み込みall()関数を変数のリストに適用するだけで簡単です (ここではブール型リテラルを使用します)。

>>> L = [True, True, True, False, True]
>>> all(L) # True, only if all elements of L are True.
False
>>> any(L) # True, if any elements of L are True.
True

あなたの言語に対応する関数はありますか (C#? Java?)。もしそうなら、それはおそらく最もクリーンなアプローチです。

于 2008-08-08T17:11:23.060 に答える
-2

これを行う場合:

if (var1 == true) {
    if (var2 == true) {
        if (var3 == true) {
            ...
        }
    }
}

それから、何かが真実でない場合にも対応できます。たとえば、入力を検証している場合、適切にフォーマットする方法などのヒントをユーザーに提供できます。

于 2008-08-08T17:13:49.730 に答える