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


148

私の質問は:

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

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

それを簡単にする方法は?


56
「アクションC」のコードを関数に入れますか?
CinCout 2017

26
これは悲しいですが、これは実際にはC ++の質問とは関係がなく、HNQに到達しました:/
YSC

2
みんな助けてくれてありがとう!最初は、すべてが問題ないことを確認したいので、ネストされたifを使用しました。これは私が推測した最も簡単な方法だからです。次回は質問してからもっと頑張っていきたいと思います。みんなが良い一日を
過ごせ

13
これは非常に優れた戦略です。最初に機能するコードを記述し、次にそれをエレガントで効率的なものにすることを心配します。
Code-Apprentice

3
@Tim答えとして投稿しました。余談ですが、投票数が減ったのは悲しいことです。
CinCout 2017

回答:


400

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

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
}

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


35
OPが自分でこれを開発する方法を理解するのを助けるために真理値表を示したことは本当に好きです。これをさらに進めて、真理値表からブール式を取得する方法を説明できますか?プログラミングとブール論理に不慣れな人にとって、これはおそらくまったく明確ではありません。
Code-Apprentice

14
評価Bに副作用がある場合、ロジックテーブルはそれを考慮する必要があります。
Yakk-Adam Nevraumont 2017

79
@Yakk私の答えは2つの理由で副作用に対処していません。まず、ソリューションは(偶然にも)正しい副作用動作をします。第2に、そしてより重要なことに、副作用があるAとBは悪いコードであり、その周辺のケースについての議論は基本的にブール論理についての質問の邪魔になるでしょう。
QuestionC

52
おそらく注目に値するかもしれませんが、A && !Bケースが何もしない場合は、!(A && !B)と同等!A || Bです。これはif (!A || B) { /* do action C */ }、空のブロックを実行して回避できることを意味します。
KRyan 2017

54
if (A && !B)将来のプログラマが維持することが本当に難しい場合、彼らを助けることは本当にありません。
レイ

65

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

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

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

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

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

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


15
@Yakk deMorganの法則を参照してください。
Code-Apprentice

@ Code-Apprentice悪い論理的思考を許してください。(!A || B)と(A &&!B)の間に違いがあるかどうかを尋ねます。どちらも私の問題では問題ないようです。あなたとQuestionCのアプローチを意味します。
starf15h 2017

6
@ Starf15hもう1つの重要な違いがあります。「アクションC」が実行される場所です。この違いにより、2つのソリューションはまったく同じになります。ここで何が起こっているのかを理解するのに役立つはずの「deMorgan's Laws」をググってみることをお勧めします。
Code-Apprentice

5
2つの解決策はまったく同じですが、実用的な違いがあるかもしれない正確に何に依存し...ています。それがまったく何もない場合(つまり、「これらの条件が満たされている場合はCを実行し、それ以外の場合は何もしない」)、elseステートメントは完全に省略できるため、これは明らかに優れたソリューションです。
Janus Bahsジャケット2017

1
また、AとBの名前に応じて、この配置はQuestionCの配置よりも人間にとって読みやすくなるか、読みにくくなる場合があります。
マイケル-クレイシャーキーはどこですか2017

15

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

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
}

7
またはもっと簡単にif (!A || B)
Tas

2
論理的には、((A && B)||!A)は(B ||!A)と同等です
Code-Apprentice

@ Code-Apprentice は、短絡のために実際にチェックせずに、isの場合のみB || !A結果になりますtrueBtrueA
CinCout

1
@CinCout良い点。理論的なブール論理の観点からは私の説明は依然として真実ですが、短絡ブール演算子の実用性は考慮していません。幸い、私自身の答えは正しい順序になっています。
Code-Apprentice

1
したがって、ロジックの観点からは、順序は重要ではありません。ただし、保守と可読性の観点からは、正確に何AB表しているかによって、大きな違いが生じる可能性があります。
Code-Apprentice

14

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

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

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


10

問題の説明:

条件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 */
}

おそらくinline、CとconstexprC ++にマークを付けますか?
アインポクルム2017

@einpoklumこの質問は実際には言語を指定していないため(ただし、Cのような構文の例を示したため)、これらの詳細の一部には触れませんでした。そのため、Cのような構文で回答しました。個人的には、条件Bが不必要に評価されないようにマクロを使用します。
jamesdlin 2017

6

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

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

これが次の3つのケースです。

  1. ネストされたdo action Cあなたの質問のロジックでは、必要condition Acondition Bするtrue-このロジックでは、我々は2に達している場合回目に用語をif-statementそして、我々はそれが知ってcondition Aいるtrue、我々はそれがある評価する必要があり、すべてのようcondition Bですtrue
  2. ネストされたelseあなたの質問のロジックで-ブロックが必要condition Aであることをtruecondition Bするfalse-私たちは達することができる唯一の方法elseならば、このロジックに-ブロックは次のようになりcondition Aましたtruecondition Bしましたfalse
  3. else質問のロジックの外側の-blockは必須condition Aであるfalse-このロジックでcondition Aはfalseの場合もdo action C

ここで私を矯正するためのコード見習いへの小道具。編集せずに正しく提示したので、彼の回答を受け入れることをお勧めします:/


2
「条件A」は、再度評価する必要がないことに注意してください。C ++では、除外された中間の法則があります。「条件A以外」が偽の場合、「条件A」は必ず真になります。
Code-Apprentice

1
短絡評価のため、がfalseのB場合にのみ評価され!Aます。したがって、elseステートメントを実行するには、両方が失敗する必要があります。
Code-Apprentice

短絡評価!A || Bがなくても、!Aとの両方Bが偽の場合は、正確に偽になります。したがって、実行A時に真になりますelse。再評価する必要はありませんA
Code-Apprentice

@ Code-Apprenticeよく臭い、すばらしい観察、私は私の答えを修正しましたが、あなたの答えを受け入れるように提案しました。私はあなたがすでに提案していることを説明しようとしています。
ジョナサンミー

それぞれのケースを説明するために、もう1票投票してください。
Code-Apprentice

6

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

f = ab +!a
f =?

実証済みの問題として、これはになりf = !a + bます。真理値表、カルノーマップなど、問題を証明するいくつかの方法があります。

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

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

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


6

すでに良い答えはありますが、この方法は、ブール代数が初めてで真理値表を評価する人にとって、より直感的であると考えました。

あなたが最初にしたいことは、Cを実行したい条件の下での外観です。これはの場合(a & b)です。また、時!a。だからあなたは(a & b) | !aます。

最小化したい場合は続行できます。「通常の」算術の場合と同様に、乗算することができます。

(a & b) | !a = (a | !a) & (b | !a)。a | !aは常にtrueなので、取り消し線を引くだけで済み、結果は最小化されます。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つの優れたアプローチは、Karnaughマップです。しかし、これについてはこれ以上詳しく説明しません。


4

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

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
  {
  ...
  }


2

Cをメソッドに抽出し、すべてのケースでできるだけ早く関数を終了します。else末尾に単一のものがある句は、可能であればほとんど常に反転する必要があります。ステップバイステップの例はここにあります:

引用C:

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

最初ifに反転して最初に取り除くelse

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

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

秒を取り除くelse

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

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

D();

そして、2つのケースが同じ本体を持ち、組み合わせることができることがわかります。

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

D();

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

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

  • いずれかC()またはD()非例外的なケースがそうならば、最後に行くべきでD()、その後反転し、例外ではif最後にもう一度


2

フラグを使用してもこの問題を解決できます

int flag = 1; 
if ( condition A ) {
    flag = 2;
    if( condition B ) {
        flag = 3;
    }
}
if(flag != 2) { 
    do action C 
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.