私の質問は:
if (/* condition A */)
{
if(/* condition B */)
{
/* do action C */
}
else
/* ... */
}
else
{
/* do action C */
}
アクションCのコードを2回ではなく1回だけ書くことは可能ですか?
単純化する方法は?
この種の問題の最初のステップは、常に論理テーブルを作成することです。
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
}
このロジックは、短いものの、将来のプログラマーが維持するのが難しい場合があることに注意してください。
次の2つのオプションがあります。
「アクションC」を実行する関数を作成します。
ネストされたifステートメントがあまり多くないように、ロジックを再配置します。 「アクションC」が発生する原因となる条件を自問してください。 「条件B」がtrueまたは「条件A」がfalseの場合に発生するように見えます。これを「NOT A OR B」と書くことができます。これをCコードに変換すると、
if (!A || B) {
action C
} else {
...
}
これらの種類の表現の詳細については、「ブール代数」、「述語論理」、および「述語計算」をグーグルで検索することをお勧めします。これらは深い数学のトピックです。すべてを学ぶ必要はなく、基本だけです。
「短絡評価」についても学ぶ必要があります。このため、元のロジックを正確に複製するには、式の順序が重要です。 B || !A
は論理的に同等ですが、これを条件として使用すると、B
の値に関係なく、A
がtrueの場合に「アクションC」が実行されます。
次のようにステートメントを簡略化できます。
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
}
パターンマッチングを使用する言語では、QuestionCの回答の真理値表をより直接的に反映する方法でソリューションを表現できます。
match (a,b) with
| (true,false) -> ...
| _ -> action c
構文に慣れていない場合、各パターンは|で表されます。 (a、b)と一致する値が続き、アンダースコアはワイルドカードとして使用され、「その他の値」を意味します。アクションc以外の操作を行う唯一のケースは、aがtrueでbがfalseの場合のみであるため、これらの値を最初のパターン(true、false)として明示的に指定し、その場合に行うべきことをすべて行います。その他の場合はすべて、「ワイルドカード」パターンに陥り、アクションを実行しますc。
問題のステートメント:
条件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 */
}
ロジックの概念では、次のようにしてこの問題を解決できます。
f = a.b +!a
f =?
実証済みの問題として、これはf = !a + b
になります。真理値表、 Karnaugh Map などのように、問題を証明する方法がいくつかあります。
したがって、Cベースの言語では、次のように使用できます。
if(!a || b)
{
// Do action C
}
追伸: Karnaugh Map は、より複雑な一連の条件にも使用されます。これは、ブール代数式を単純化する方法です。
すでに良い答えがありますが、ブール代数に慣れていない人にとっては、このアプローチは真理値表を評価するよりもさらに直感的だと思いました。
あなたが最初にしたいことは、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つの優れたアプローチは、カルノーマップです。しかし、私はこれについてこれ以上深く入りません。
うーん、これも私をつまずかせましたが、 Code-Apprenticeが指摘した のように、do action C
を実行するか、nested -else
ブロックを実行する必要があることが保証されているため、コードは次のように簡略化できます:
if (not condition A or condition B) {
do action C
} else {
...
}
これが、3つのケースに当てはまる方法です。
do action C
には、condition A
とcondition B
がtrue
である必要がありました-このロジックでは、2nd if
- statement内の用語は、condition A
がtrue
であることを知っているので、評価する必要があるのはcondition B
がtrue
であるということだけです。else
- blockには、condition A
がtrue
であり、condition B
がfalse
である必要がありました-このロジックでelse
- blockに到達できる唯一の方法は、condition A
がtrue
およびcondition B
がfalse
だった場合ですelse
- blockには、condition A
がfalse
である必要がありました-このロジックでは、condition A
がfalseの場合、do action C
もここで私をまっすぐにしてくれたCode-Apprenticeの小道具。 彼の答え を受け入れることをお勧めします。編集せずに正しく提示したからです:/
コードをテキストのように見せるには、ブールフラグを使用します。ロジックが特に不明瞭な場合は、コメントを追加してください。
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
{
...
}
if((A && B ) || !A)
{
//do C
}
else if(!B)
{
//...
}
メソッドに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回反転します
フラグを使用すると、この問題も解決できます
int flag = 1;
if ( condition A ) {
flag = 2;
if( condition B ) {
flag = 3;
}
}
if(flag != 2) {
do action C
}