web-dev-qa-db-ja.com

条件Aが一致した場合、アクションCを実行するには条件Bを一致させる必要があります

私の質問は:

if (/* condition A */)
{
    if(/* condition B */)
      {
         /* do action C */
      }
    else
      /* ... */
}
else
{
   /* do action C */
}

アクションCのコードを2回ではなく1回だけ書くことは可能ですか?

単純化する方法は?

146
starf15h

この種の問題の最初のステップは、常に論理テーブルを作成することです。

A | B | Result
-------------------
T | T | do action C
T | F | ...
F | T | do action C
F | F | do action C

テーブルを作成したら、解決策は明確です。

if (A && !B) {
  ...
}
else {
  do action C
}

このロジックは、短いものの、将来のプログラマーが維持するのが難しい場合があることに注意してください。

399
QuestionC

次の2つのオプションがあります。

  1. 「アクションC」を実行する関数を作成します。

  2. ネストされたifステートメントがあまり多くないように、ロジックを再配置します。 「アクションC」が発生する原因となる条件を自問してください。 「条件B」がtrueまたは「条件A」がfalseの場合に発生するように見えます。これを「NOT A OR B」と書くことができます。これをCコードに変換すると、

    if (!A || B) {
        action C
    } else {
        ...
    }
    

これらの種類の表現の詳細については、「ブール代数」、「述語論理」、および「述語計算」をグーグルで検索することをお勧めします。これらは深い数学のトピックです。すべてを学ぶ必要はなく、基本だけです。

「短絡評価」についても学ぶ必要があります。このため、元のロジックを正確に複製するには、式の順序が重要です。 B || !Aは論理的に同等ですが、これを条件として使用すると、Bの値に関係なく、Aがtrueの場合に「アクションC」が実行されます。

65
Code-Apprentice

次のようにステートメントを簡略化できます。

if ((A && B) || (!A)) // or simplified to (!A || B) as suggested in comments
{
    do C
}

それ以外の場合は、「C」のコードを別の関数に入れて呼び出します。

DoActionC()
{
    ....
    // code for Action C
}
if (condition A)
{
    if(condition B)
      {
         DoActionC(); // call the function
      }
    else
      ...
}
else
{
   DoActionC(); // call the function
}
15
CinCout

パターンマッチングを使用する言語では、QuestionCの回答の真理値表をより直接的に反映する方法でソリューションを表現できます。

match (a,b) with
| (true,false) -> ...
| _ -> action c

構文に慣れていない場合、各パターンは|で表されます。 (a、b)と一致する値が続き、アンダースコアはワイルドカードとして使用され、「その他の値」を意味します。アクションc以外の操作を行う唯一のケースは、aがtrueでbがfalseの場合のみであるため、これらの値を最初のパターン(true、false)として明示的に指定し、その場合に行うべきことをすべて行います。その他の場合はすべて、「ワイルドカード」パターンに陥り、アクションを実行しますc。

14

問題のステートメント:

条件Aが一致した場合、アクションCを実行するには条件Bを一致させる必要があります

記述含意A含意B!A || Bと同等の論理命題(他の回答で述べたように):

bool implies(bool p, bool q) { return !p || q; }

if (implies(/* condition A */,
            /* condition B */))
{
    /* do action C */
}
10
jamesdlin

ロジックの概念では、次のようにしてこの問題を解決できます。

f = a.b +!a
f =?

実証済みの問題として、これはf = !a + bになります。真理値表、 Karnaugh Map などのように、問題を証明する方法がいくつかあります。

したがって、Cベースの言語では、次のように使用できます。

if(!a || b)
{
   // Do action C
}

追伸: Karnaugh Map は、より複雑な一連の条件にも使用されます。これは、ブール代数式を単純化する方法です。

6
Siyavash Hamdi

すでに良い答えがありますが、ブール代数に慣れていない人にとっては、このアプローチは真理値表を評価するよりもさらに直感的だと思いました。

あなたが最初にしたいことは、Cを実行する条件の下で見ることです。これは(a & b)の場合です。また、!aの場合。したがって、(a & b) | !aがあります。

最小化する場合は、続行できます。 「通常の」算術のように、乗算することができます。

(a & b) | !a = (a | !a) & (b | !a)。 | !aは常に真であるため、それを消すだけで済み、結果が最小化されます:b | !a。順序に違いがある場合、!aがtrueの場合にのみbをチェックするため(たとえば、!aがNULLポインターチェックで、bがコメントで指摘された@LordFarquaadのようなポインターに対する操作である場合)、 2つを切り替えたい。

他のケース(/ * ... * /)は、cが実行されない場合は常に実行されるため、elseケースに入れることができます。

また、言及する価値があるのは、おそらくアクションcをメソッドに入れるいずれの方法にも意味があるということです。

次のコードが残ります。

if (!A || B)
{
    doActionC()  // execute method which does action C
}
else
{
   /* ... */ // what ever happens here, you might want to put it into a method, too.
}

この方法では、より多くのオペランドで項を最小化することもできます。これは、真理値表ではすぐに見苦しくなります。もう1つの優れたアプローチは、カルノーマップです。しかし、私はこれについてこれ以上深く入りません。

6
deetz

うーん、これも私をつまずかせましたが、 Code-Apprenticeが指摘した のように、do action Cを実行するか、nested -elseブロックを実行する必要があることが保証されているため、コードは次のように簡略化できます:

if (not condition A or condition B) {
    do action C
} else {
    ...
}

これが、3つのケースに当てはまる方法です。

  1. 質問のロジックにネストされたdo action Cには、condition Acondition Btrueである必要がありました-このロジックでは、2nd if- statement内の用語は、condition Atrueであることを知っているので、評価する必要があるのはcondition Btrueであるということだけです。
  2. 質問のロジックのネストされたelse- blockには、condition Atrueであり、condition Bfalseである必要がありました-このロジックでelse- blockに到達できる唯一の方法は、condition Atrueおよびcondition Bfalseだった場合です
  3. 質問のロジックの外側のelse- blockには、condition Afalseである必要がありました-このロジックでは、condition Aがfalseの場合、do action C

ここで私をまっすぐにしてくれたCode-Apprenticeの小道具。 彼の答え を受け入れることをお勧めします。編集せずに正しく提示したからです:/

6
Jonathan Mee

コードをテキストのように見せるには、ブールフラグを使用します。ロジックが特に不明瞭な場合は、コメントを追加してください。

bool do_action_C;

// Determine whether we need to do action C or just do the "..." action
// If condition A is matched, condition B needs to be matched in order to do action C
if (/* condition A */)
{
    if(/* condition B */)
      do_action_C = true; // have to do action C because blah
    else
      do_action_C = false; // no need to do action C because blarg
}
else
{
  do_action_C = true; // A is false, so obviously have to do action C
}

if (do_action_C)
  {
     DoActionC(); // call the function
  }
else
  {
  ...
  }
4
anatolyg
if((A && B ) || !A)
{
  //do C
}
else if(!B)
{
  //...
}
3
Ali

メソッドにCを抽出し、すべての場合にできるだけ早く関数を終了します。末尾に単一のものが含まれるelse句は、可能な限りほとんど常に逆にする必要があります。以下に、ステップバイステップの例を示します。

抽出C:

if (A) {
   if (B)
      C();
   else
      D();
} else
   C();

最初のifを反転させて、最初のelseを削除します。

if (!A) {
   C();
   return;
}

if (B)
   C();
else
   D();

2番目のelseを取り除きます:

if (!A) {
   C();
   return;
}

if (B) {
   C();
   return;
} 

D();

そして、あなたは2つのケースが同じボディを持ち、組み合わせることができることに気付くことができます:

if (!A || B) {
   C();
   return;
}

D();

改善すべきオプションは次のとおりです。

  • コンテキストに依存しますが、!A || Bがわかりにくい場合は、1つ以上の変数に抽出して意図を説明します

  • C()またはD()のいずれかが例外でない場合は最後に行く必要があるため、D()が例外である場合、ifを最後に1回反転します

2
Dave Cousineau

フラグを使用すると、この問題も解決できます

int flag = 1; 
if ( condition A ) {
    flag = 2;
    if( condition B ) {
        flag = 3;
    }
}
if(flag != 2) { 
    do action C 
}
2
Spr k