Wenn Bedingung A übereinstimmt, muss Bedingung B erfüllt sein, um Aktion C auszuführen

148

Meine Frage ist:

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

Ist es möglich, den Aktionscode C nur einmal statt zweimal zu schreiben?

Wie kann man es vereinfachen?

starf15h
quelle
56
Den Code für "Aktion C" in eine Funktion einfügen?
CinCout
26
Das ist traurig, dass dies nicht wirklich mit C ++ zu tun hat. Die Frage wurde an HNQ gestellt: /
YSC
2
Vielen Dank an alle, die mir geholfen haben! Am Anfang möchte ich nur sicherstellen, dass alles in Ordnung ist, also habe ich ein verschachteltes if verwendet. Es ist, weil dies der einfachste Weg ist, den ich erraten habe. Ich werde versuchen, mehr Anstrengungen zu unternehmen, nachdem ich das nächste Mal Fragen gestellt habe. Wünsche allen einen schönen Tag :)
starf15h
13
Das ist eine sehr gute Strategie: Schreiben Sie Code, der zuerst funktioniert, und sorgen Sie sich später darum, ihn später elegant und effizient zu gestalten.
Code-Apprentice
3
@ Tim Ich habe es als Antwort gepostet. Nebenbei bemerkt ist es traurig, dort weniger Stimmen zu sehen.
CinCout

Antworten:

400

Ihr erster Schritt bei solchen Problemen besteht immer darin, eine Logiktabelle zu erstellen.

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

Sobald Sie die Tabelle erstellt haben, ist die Lösung klar.

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

Beachten Sie, dass diese Logik zwar kürzer ist, für zukünftige Programmierer jedoch möglicherweise schwierig beizubehalten ist.

FrageC
quelle
35
Ich mag es wirklich, dass Sie die Wahrheitstabelle gezeigt haben, um dem OP zu helfen, zu verstehen, wie man dies selbst entwickelt. Können Sie noch einen Schritt weiter gehen und erklären, wie Sie den booleschen Ausdruck aus der Wahrheitstabelle erhalten? Für jemanden, der neu in der Programmierung und der booleschen Logik ist, ist dies wahrscheinlich überhaupt nicht klar.
Code-Lehrling
14
Wenn die Auswertung BNebenwirkungen hat, muss die Logiktabelle dies berücksichtigen.
Yakk - Adam Nevraumont
79
@Yakk Meine Antwort geht aus zwei Gründen nicht auf Nebenwirkungen ein. Erstens hat die Lösung (zufällig) das richtige Nebenwirkungsverhalten. Zweitens, und was noch wichtiger ist, wären A und B mit Nebenwirkungen schlechter Code, und eine Diskussion über diesen Randfall wäre eine Ablenkung für eine Frage, die sich grundlegend mit der Booleschen Logik befasst.
QuestionC
52
Vielleicht erwähnenswert, falls A && !Bes sich um ein No-Op handelt: !(A && !B)Dies entspricht, !A || Bwas bedeutet, dass Sie if (!A || B) { /* do action C */ }einen leeren Block ausführen und vermeiden können.
KRyan
54
Wenn if (A && !B)es für zukünftige Programmierer wirklich schwierig ist, sie zu warten, gibt es wirklich keine Hilfe für sie.
Ray
65

Sie haben zwei Möglichkeiten:

  1. Schreiben Sie eine Funktion, die "Aktion C" ausführt.

  2. Ordnen Sie Ihre Logik neu an, damit Sie nicht so viele verschachtelte if-Anweisungen haben. Fragen Sie sich, unter welchen Bedingungen "Aktion C" auftritt. Es sieht für mich so aus, als ob es passiert, wenn entweder "Bedingung B" wahr oder "Bedingung A" falsch ist. Wir können dies als "NICHT A ODER B" schreiben. Wenn wir dies in C-Code übersetzen, erhalten wir

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

Um mehr über diese Art von Ausdrücken zu erfahren, schlage ich vor, "Boolesche Algebra", "Prädikatenlogik" und "Prädikatenrechnung" zu googeln. Dies sind tiefe mathematische Themen. Sie müssen nicht alles lernen, nur die Grundlagen.

Sie sollten auch etwas über "Kurzschlussauswertung" lernen. Aus diesem Grund ist die Reihenfolge der Ausdrücke wichtig, um Ihre ursprüngliche Logik genau zu duplizieren. Während dies B || !Alogisch äquivalent ist, führt die Verwendung als Bedingung "Aktion C" aus, wenn dies Bwahr ist, unabhängig vom Wert von A.

Code-Lehrling
quelle
15
@ Yakk Siehe deMorgans Gesetze.
Code-Lehrling
@ Code-Apprentice Bitte vergib mir mein schlechtes logisches Denken. Ich möchte fragen, ob es einen Unterschied zwischen (! A || B) und (A &&! B) gibt. Es scheint, dass beide für mein Problem in Ordnung sind. Ich meine Ihren und den Ansatz von QuestionC.
starf15h
6
@ Starf15h Es gibt noch einen weiteren entscheidenden Unterschied: Wo die "Aktion C" ausgeführt wird. Dieser Unterschied macht unsere beiden Lösungen genau gleichwertig. Ich schlage vor, dass Sie "deMorgan's Laws" googeln, um zu verstehen, was hier vor sich geht.
Code-Apprentice
5
Die beiden Lösungen sind genau gleichwertig, es kann jedoch einen praktischen Unterschied geben, je nachdem, was genau ...ist . Wenn es überhaupt nichts ist (dh "mach C, wenn diese Bedingungen erfüllt sind; sonst nichts"), dann ist dies eindeutig die überlegene Lösung, da die elseAussage dann einfach ganz weggelassen werden kann.
Janus Bahs Jacquet
1
Abhängig von den Namen von A und B kann diese Anordnung für einen Menschen besser oder weniger lesbar sein als die Anordnung von QuestionC.
Michael - Wo ist Clay Shirky
15

Sie können die Aussage folgendermaßen vereinfachen:

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

Andernfalls geben Sie den Code für 'C' in eine separate Funktion ein und rufen Sie ihn auf:

DoActionC()
{
    ....
    // code for Action C
}
if (condition A)
{
    if(condition B)
      {
         DoActionC(); // call the function
      }
    else
      ...
}
else
{
   DoActionC(); // call the function
}
CinCout
quelle
7
Oder einfacherif (!A || B)
Tas
2
Logischerweise entspricht ((A && B) ||! A) (B ||! A)
Code-Apprentice
@ Code-Apprentice B || !Awird truenur dann resultieren, wenn dies der BtrueA
Fall
1
@CinCout Guter Punkt. Obwohl meine Aussage aus theoretischer Sicht der booleschen Logik immer noch zutrifft, habe ich die praktischen Aspekte der booleschen Kurzschlussoperatoren nicht berücksichtigt. Zum Glück hat meine eigene Antwort die richtige Reihenfolge.
Code-Lehrling
1
Aus logischer Sicht spielt die Reihenfolge also keine Rolle. Unter dem Gesichtspunkt der Wartung und Lesbarkeit kann es jedoch einen großen Unterschied geben, je nachdem, was genau Aund wofür Bsteht.
Code-Apprentice
14

In einer Sprache mit Mustervergleich können Sie die Lösung so ausdrücken, dass sie die Wahrheitstabelle in der Antwort von QuestionC direkter widerspiegelt.

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

Wenn Sie mit der Syntax nicht vertraut sind, wird jedes Muster durch ein | dargestellt gefolgt von den Werten, die mit (a, b) übereinstimmen sollen, und der Unterstrich wird als Platzhalter verwendet, um "alle anderen Werte" zu bedeuten. Da der einzige Fall, in dem wir etwas anderes als Aktion c tun möchten, der Fall ist, in dem a wahr und b falsch ist, geben wir diese Werte explizit als erstes Muster an (wahr, falsch) und tun dann, was in diesem Fall getan werden sollte. In allen anderen Fällen fallen wir zum "Platzhalter" -Muster durch und führen Aktion c aus.

Aaron M. Eshbach
quelle
10

Die Problemstellung:

Wenn Bedingung A übereinstimmt, muss Bedingung B erfüllt sein, um Aktion C auszuführen

beschreibt Implikation : A impliziert B , ein logischer Satz, der äquivalent zu !A || B(wie in anderen Antworten erwähnt) ist:

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

if (implies(/* condition A */,
            /* condition B */))
{
    /* do action C */
}
Jamesdlin
quelle
Vielleicht inlinefür C und constexprauch für C ++ markieren ?
Einpoklum
@einpoklum Ich bin auf einige dieser Details nicht eingegangen, weil diese Frage keine Sprache spezifizierte (aber ein Beispiel mit C-ähnlicher Syntax gab), also gab ich eine Antwort mit C-ähnlicher Syntax. Persönlich würde ich ein Makro verwenden, damit Bedingung B nicht unnötig ausgewertet wird.
Jamesdlin
6

Ugh, das hat mich auch gestolpert, aber wie Code-Apprenticedo action C betont hat else, müssen wir den verschachtelten Block garantiert ausführen oder ausführen , sodass der Code vereinfacht werden kann, um:

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

So treffen wir die 3 Fälle:

  1. Die verschachtelte do action Cin der Logik Ihrer Frage ist erforderlich condition Aund condition Bwerden true- In dieser Logik, wenn wir die 2 erreichen nd Begriff in der if-Anweisung dann wissen wir , dass condition Aist truealso alle müssen wir bewerten ist , dass condition Bisttrue
  2. Die verschachtelte else-Block in Ihrer Logik der Frage erforderlich condition Asein trueund condition Bzu sein false- der einzige Weg , dass wir das erreichen können else-Block in dieser Logik würde , wenn sein condition Awaren trueund condition Bwarenfalse
  3. Der äußere elseBlock in der Logik Ihrer Frage muss condition Asein false- In dieser Logik, wenn condition Afalsch ist, sind wir auchdo action C

Props an Code-Apprentice, der mich hier rausgebracht hat. Ich würde vorschlagen, seine Antwort zu akzeptieren , da er sie korrekt präsentiert hat, ohne sie zu bearbeiten: /

Jonathan Mee
quelle
2
Beachten Sie, dass "Bedingung A" nicht erneut ausgewertet werden muss. In C ++ haben wir das Gesetz der ausgeschlossenen Mitte. Wenn "nicht Bedingung A" falsch ist, dann ist "Bedingung A" notwendigerweise wahr.
Code-Lehrling
1
BWird wegen Kurzschlussauswertung nur ausgewertet, wenn sie !Afalsch ist. Beide müssen also fehlschlagen, um die elseAnweisungen auszuführen .
Code-Lehrling
Auch ohne Kurzschluss ist die Auswertung !A || Bgenau dann falsch, wenn beide !Aund Bfalsch sind. Daher Awird wahr sein, wenn die elseausgeführt wird. Keine Notwendigkeit zur Neubewertung A.
Code-Apprentice
@ Code-Apprentice Gut stinken, ausgezeichnete Beobachtung, ich habe meine Antwort korrigiert, aber vorgeschlagen, Ihre zu akzeptieren. Ich versuche nur zu erklären, was Sie bereits vorgebracht haben.
Jonathan Mee
Ich wünschte, ich könnte Ihnen eine weitere Stimme geben, um jeden Fall zu erklären.
Code-Lehrling
6

Im Logikkonzept können Sie dieses Problem wie folgt lösen:

f = ab +! a
f =?

Als nachgewiesenes Problem führt dies zu f = !a + b. Es gibt einige Möglichkeiten, das Problem zu beweisen, wie z. B. Wahrheitstabelle, Karnaugh Map und so weiter.

In C-basierten Sprachen können Sie also Folgendes verwenden:

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

PS: Karnaugh Map wird auch für kompliziertere Serien von Bedingungen verwendet. Es ist eine Methode zur Vereinfachung von Booleschen Algebra-Ausdrücken.

Siyavash Hamdi
quelle
6

Obwohl es bereits gute Antworten gibt, dachte ich, dass dieser Ansatz für jemanden, der neu in der Booleschen Algebra ist, noch intuitiver sein könnte, als eine Wahrheitstabelle auszuwerten.

Als erstes möchten Sie prüfen, unter welchen Bedingungen Sie C ausführen möchten. Dies ist der Fall, wenn (a & b). Auch wenn !a. Also hast du (a & b) | !a.

Wenn Sie minimieren möchten, können Sie fortfahren. Genau wie bei "normalen" Arithmetiken können Sie multiplizieren.

(a & b) | !a = (a | !a) & (b | !a). a | ! a ist immer wahr, Sie können es also einfach streichen, wodurch Sie das minimierte Ergebnis erhalten : b | !a. Falls die Reihenfolge einen Unterschied macht, weil Sie b nur prüfen möchten, wenn! A wahr ist (zum Beispiel, wenn! A eine Nullzeigerprüfung ist und b eine Operation auf dem Zeiger ist, wie in seinem Kommentar auf @LordFarquaad hingewiesen), könnten Sie dies tun will die beiden wechseln.

Der andere Fall (/ * ... * /) wird immer ausgeführt, wenn c nicht ausgeführt wird, also können wir ihn einfach in den else-Fall setzen.

Erwähnenswert ist auch, dass es wahrscheinlich in beiden Fällen sinnvoll ist, Aktion c in eine Methode einzufügen.

Was uns den folgenden Code lässt:

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

Auf diese Weise können Sie auch Begriffe mit mehr Operanden minimieren, was bei Wahrheitstabellen schnell hässlich wird. Ein weiterer guter Ansatz sind Karnaugh-Karten. Aber ich werde jetzt nicht tiefer darauf eingehen.

deetz
quelle
4

Verwenden Sie boolesche Flags, damit der Code eher wie Text aussieht. Wenn die Logik besonders unklar ist, fügen Sie Kommentare hinzu.

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
  {
  ...
  }
Anatolyg
quelle
3
if((A && B ) || !A)
{
  //do C
}
else if(!B)
{
  //...
}
Ali
quelle
2

Ich würde C zu einer Methode extrahieren und dann die Funktion in allen Fällen so schnell wie möglich beenden. elseKlauseln mit einer einzigen Sache am Ende sollten nach Möglichkeit fast immer invertiert werden. Hier ist ein schrittweises Beispiel:

Auszug C:

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

Zuerst umkehren if, um zuerst loszuwerden else:

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

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

Zweitens loswerden else:

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

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

D();

Und dann können Sie feststellen, dass die beiden Fälle den gleichen Körper haben und kombiniert werden können:

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

D();

Optionale Verbesserungsmöglichkeiten wären:

  • hängt vom Kontext ab, aber wenn !A || Bes verwirrend ist, extrahieren Sie es in eine oder mehrere Variablen, um die Absicht zu erklären

  • Welcher C()oder welcher D()nicht außergewöhnliche Fall der letzte ist, sollte der letzte sein. Wenn dies D()die Ausnahme ist, kehren Sie das ifletzte Mal um

Dave Cousineau
quelle
2

Die Verwendung von Flags kann dieses Problem ebenfalls lösen

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