Warum würden Sie eine Aufgabe in einer Bedingung verwenden?

79

In vielen Sprachen sind Aufträge unter bestimmten Bedingungen legal. Ich habe den Grund dafür nie verstanden. Warum würdest du schreiben:

if (var1 = var2) {
  ...
}

anstatt:

var1 = var2;
if (var1) {
  ...
}
lajos
quelle
Ist diese Frage wirklich so sprachunabhängig? Welche Sprachen sind davon betroffen? Ich weiß, dass C und C ++ sind, was noch?
Wolf

Antworten:

113

Es ist für Schleifen nützlicher als für if-Anweisungen.

while( var = GetNext() )
{
  ...do something with var 
}

Was sonst geschrieben werden müsste

var = GetNext();
while( var )
{
 ...do something
 var = GetNext();
}
Gerald
quelle
14
Ich stimme zu - würde aber schreiben: while ((var = GetNext ())! = 0) {...} ohne nachzudenken, auch weil GCC sich beschwert, wenn ich meine Standardkompilierungsoptionen nicht benutze.
Jonathan Leffler
1
Wahr. Angenommen, Sie verwenden eine Sprache, die for-loop-Deklarationen unterstützt, und Sie verwenden var nicht bereits außerhalb der Schleife.
Gerald
1
In einigen Sprachen, wie ANSI C, erlauben Scoping-Regeln diesen Ansatz nicht, @KerrekSB
wirrbel
7
@wirrbel: Du meinst ANSI C von was, 1989? Zum Beispiel als sie Dampfmaschinen und Lochkarten hatten? Das mag der Fall sein, aber ist das relevant?
Kerrek SB
3
Diese Warnung ist nicht darauf zurückzuführen, dass mit einer Aufgabe in einer Weile etwas nicht stimmt, sondern darauf, dass eine Aufgabe, wenn Sie wirklich einen Vergleich durchführen wollten, ein häufiger Fehler ist. Wenn Sie die Warnung loswerden möchten, können Sie das JS-Äquivalent dessen tun, was @Jonathan Leffler vorschlägt. Persönlich würde ich das wahrscheinlich sowieso tun, weil ich den JavaScript-Wahrheitsregeln sehr misstraue :) Es sollte auch in einigen Sprachen wie C # und Java beachtet werden, dass dies der einzige Weg ist.
Gerald
32

Ich finde es am nützlichsten in Aktionsketten, die häufig eine Fehlererkennung usw. beinhalten.

if ((rc = first_check(arg1, arg2)) != 0)
{
    report error based on rc
}
else if ((rc = second_check(arg2, arg3)) != 0)
{
    report error based on new rc
}
else if ((rc = third_check(arg3, arg4)) != 0)
{
    report error based on new rc
}
else
{
    do what you really wanted to do
}

Die Alternative (ohne Verwendung der Zuordnung in der Bedingung) ist:

rc = first_check(arg1, arg2);
if (rc != 0)
{
    report error based on rc
}
else
{
    rc = second_check(arg2, arg3);
    if (rc != 0)
    {
        report error based on new rc
    }
    else
    {
        rc = third_check(arg3, arg4);
        if (rc != 0)
        {
            report error based on new rc
        }
        else
        {
            do what you really wanted to do
        }
    }
}

Bei langwieriger Fehlerprüfung kann die Alternative von der rechten Seite der Seite ausgeführt werden, während die Version mit bedingter Zuweisung dies nicht tut.

Die Kontrollen Fehler könnte auch sein , ‚Aktionen‘ - first_action(), second_action(), third_action()- natürlich nicht nur überprüft. Das heißt, sie könnten Schritte in dem Prozess sein, den die Funktion verwaltet. (Meistens befinden sich die Funktionen in dem Code, mit dem ich arbeite, im Sinne von Vorbedingungsprüfungen oder Speicherzuweisungen, die für die Funktion der Funktion erforderlich sind, oder in ähnlicher Weise).

Jonathan Leffler
quelle
Ja, ich habe genau das getan, um übermäßiges Verschachteln zu vermeiden, und dann online gesucht, um festzustellen, ob dies eine gängige Praxis ist. Am Ende habe ich mich dagegen entschieden, weil ich eigentlich nur einen anderen Fall hatte, aber dies scheint ein legitimer Grund zu sein, den Auftrag in die Bedingung zu stellen.
Ibrahim
Mitarbeiter hassen es! Zumindest in den meisten Fällen ist es für mich weitaus wartbarer als ein riesiger Baum oder viele Erträge. Ich bevorzuge eine einzelne Rückkehr von einer Funktion ...
Nathan
Ein vernünftiges Beispiel: Die Begleitblöcke machen das Muster offensichtlich und selbsterklärend. Aber der In-Block-Code ("Pseudo") sieht ein bisschen hässlich aus, und der umgekehrte Stil (Platzieren, was Sie am Ende wirklich tun wollten ) ist nicht so toll.
Wolf
31

Es ist nützlicher, wenn Sie eine Funktion aufrufen:

if (n = foo())
{
    /* foo returned a non-zero value, do something with the return value */
} else {
    /* foo returned zero, do something else */
}

Sicher, Sie können einfach n = foo () setzen; auf einer separaten Aussage dann wenn (n), aber ich denke, das obige ist eine ziemlich lesbare Redewendung.

Chris Young
quelle
3
Ich denke, Sie sollten es in Klammern setzen, oder gcc wird sich beschweren mit: Warnung: Klammern um die als Wahrheitswert verwendete Zuordnung vorschlagen [-Wparentheses] So: if ((n = foo ())) {// etwas mit dem Rückgabewert tun } else {// foo hat null / NULL zurückgegeben, mach etwas anderes}
Fabio Pozzi
23

Dies kann nützlich sein, wenn Sie eine Funktion aufrufen, die entweder Daten zur Bearbeitung oder ein Flag zurückgibt, um einen Fehler anzuzeigen (oder wenn Sie fertig sind).

Etwas wie:

while ((c = getchar()) != EOF) {
    // process the character
}

// end of file reached...

Persönlich ist es eine Redewendung, die ich nicht besonders mag, aber manchmal ist die Alternative hässlicher.

Michael Burr
quelle
13

GCC kann Ihnen helfen, (mit -Wall) zu erkennen, ob Sie unbeabsichtigt versuchen, eine Zuweisung als Wahrheitswert zu verwenden, falls empfohlen wird, dass Sie schreiben

if ((n = foo())) {
   ...
}

Verwenden Sie zusätzliche Klammern, um anzuzeigen, dass dies wirklich das ist, was Sie wollen.

JesperE
quelle
1
Gut zu wissen. Ich hoffe, Ihre Mitarbeiter lesen es genauso.
Wolf
1
Mich? Meine Güte, ich würde niemals versuchen, die Zuordnung als Wahrheitswert zu verwenden. Ich bezog mich auf das, was GCC empfiehlt.
JesperE
GCC kann auch Fortran kompilieren . Vielleicht explizit über die Programmiersprache?
Peter Mortensen
9

Die Redewendung ist nützlicher, wenn Sie eine whileSchleife anstelle einer ifAnweisung schreiben . Für eine ifAussage können Sie sie wie beschrieben aufteilen. Aber ohne dieses Konstrukt müssten Sie sich entweder wiederholen:

c = getchar();
while (c != EOF) {
    // ...
    c = getchar();
}

oder verwenden Sie eine anderthalb-Schleifen-Struktur:

while (true) {
    c = getchar();
    if (c == EOF) break;
    // ...
}

Normalerweise würde ich die eineinhalb Loop-Form bevorzugen.

Greg Hewgill
quelle
1
whileVerwenden Sie für eine Schleife lieber:do { c = getchar(); ... } while (c != EOF);
Scott S
Und forSchleifen können stattdessen verwendet werden: for (char c = getchar(); c != EOF; c= getchar()) { /* do something with c */ }- das ist die prägnanteste, und forsagt immer ‚Loop‘ mir, stilistisch. Ein kleiner Nachteil ist, dass Sie die Funktion angeben müssen, die czweimal zurückgibt.
Scott S
4

Die kurze Antwort lautet, dass ausdrucksorientierte Programmiersprachen prägnanteren Code ermöglichen. Sie zwingen Sie nicht, Befehle von Abfragen zu trennen .

Hugh Allen
quelle
1
Es ist ein ständiger Kampf, die Codierung ohne Zuweisungen in if (), while () und anderen ähnlichen Anweisungen durchzusetzen. C / C ++ ist bekannt für Nebenwirkungen, die sich häufig als Fehler herausstellen, insbesondere wenn wir die Operatoren ++ und - hinzufügen.
Alexis Wilke
1
Der erste Link ist nutzlos: C oder C ++ scheinen keine solche Sprache zu sein.
Wolf
3

In PHP ist es beispielsweise nützlich, um SQL-Datenbankergebnisse zu durchlaufen:

while ($row = mysql_fetch_assoc($result)) {
    // Display row
}

Das sieht viel besser aus als:

$row = mysql_fetch_assoc($result);
while ($row) {
    // Display row
    $row = mysql_fetch_assoc($result);
}
Jeremy Ruten
quelle
4
Dies hat auch den netten Nebeneffekt, dass $ row nicht über diese Schleife hinaus lebt und daher früher für die Speicherbereinigung in Frage kommt (in Sprachen, die sowieso GC ausführen).
Darren Greaves
2

Der andere Vorteil ergibt sich aus der Verwendung von gdb. Im folgenden Code ist der Fehlercode nicht bekannt, wenn es sich um einen einzelnen Schritt handelt.

while (checkstatus() != -1) {
    // process
}

Lieber

while (true) {
    int error = checkstatus();
    if (error != -1)
        // process
    else
        //fail
}

Jetzt können wir im Einzelschritt anhand von checkstatus () den Rückgabefehlercode ermitteln.

Plasmixe
quelle
1
Sie meinen, dass es für temporäre Änderungen beim Debuggen von Code hilfreich sein kann?
Wolf
0

Ich finde es sehr nützlich bei Funktionen, die Optionen zurückgeben ( boost::optionaloder std::optionalin C ++ 17):

std::optional<int> maybe_int(); // function maybe returns an int

if (auto i = maybe_int()) {
    use_int(*i);
}

Dies reduziert den Umfang meiner Variablen, macht den Code kompakter und beeinträchtigt nicht die Lesbarkeit (finde ich).

Gleiches gilt für Zeiger:

int* ptr_int();

if (int* i = ptr_int()) {
    use_int(*i);
}
Julien-L
quelle
Übrigens, wenn Sie den Umfang der Variablen einschränken möchten, können Sie if mit Initialisierern verwenden :)
cigien
-2

Der Grund ist:

  1. Leistungsverbesserung (manchmal)

  2. Weniger Code (immer)

Nehmen Sie ein Beispiel: Es gibt eine Methode someMethod()und in einer ifBedingung möchten Sie überprüfen, ob der Rückgabewert der Methode ist null. Wenn nicht, werden Sie den Rückgabewert erneut verwenden.

If(null != someMethod()){
    String s = someMethod();
    ......
    //Use s
}

Dies beeinträchtigt die Leistung, da Sie dieselbe Methode zweimal aufrufen. Verwenden Sie stattdessen:

String s;
If(null != (s = someMethod())) {
    ......
    //Use s
}
Gangadhar
quelle
Welche Sprache ist das? Es würde nicht in C # oder C ++ kompiliert (Großschreibung von "If").
Peter Mortensen