Soll ich switch-Anweisungen verwenden oder long if… else chains?

36

Oft, wenn ich von der switch-Anweisung höre, wird sie verschoben, um lange if ... else-Ketten zu ersetzen. Aber es scheint, dass ich mit der switch-Anweisung mehr Code schreibe, als ich nur schreiben würde, wenn ... sonst. Sie haben auch andere Probleme, z. B. das Beibehalten aller Variablen für alle Aufrufe im selben Bereich .

Hier ist ein Code, der den Ablauf darstellt, den ich normalerweise schreibe ( dank diam )

String comment;   // The generated insult.
int which = (int)(Math.random() * 3);  //  Result is 0, 1, or 2.

if (which == 0) {
    comment = "You look so much better than usual.";
} else if (which == 1) {
    comment = "Your work is up to its usual standards.";
} else if (which == 2) {
    comment = "You're quite competent for so little experience.";
} else {
    comment = "Oops -- something is wrong with this code.";
}

Dann wollen sie, dass ich das durch Folgendes ersetze:

String comment;   // The generated insult.
int which = (int)(Math.random() * 3);  //  Result is 0, 1, or 2.

switch (which) {
    case 0:  
             comment = "You look so much better than usual.";
    break;
    case 1:  
             comment = "Your work is up to its usual standards.";
    break;
    case 2:  
             comment = "You're quite competent for so little experience.";
    break;
    default: 
             comment = "Oops -- something is wrong with this code.";
}

Scheint viel mehr Code in einer viel umständlicheren Syntax zu haben. Aber hat die Verwendung der switch-Anweisung wirklich einen Vorteil?

TheLQ
quelle
Pfui. Ja, das ist sicherlich umfangreicher, aber nur in der C-Familie, weil die Syntax für case-Anweisungen so hässlich ist.
Mason Wheeler
5
Über solche Dinge wurde im Zusammenhang mit stackoverflow viel diskutiert: - stackoverflow.com/questions/449273/… - stackoverflow.com/questions/767821/… - stackoverflow.com/questions/97987/switch-vs-if-else
Yevgeniy Brikman
1
Sie sollten beide nach Möglichkeit vermeiden. Es ist viel besser, eine Datenstruktur zu erstellen und eine Suche durchzuführen, selbst wenn das Suchziel eine Funktion oder eine Klasse ist.
Kevin Cline
Schalter ist schneller, zumindest in .net. Ich weiß nicht über Java.
Knerd
Kann zu einem Wörterbuch der Fälle und Methoden und einem "wenn"
Pavel Yermalovich

Antworten:

57

Für diese besondere Situation scheint es mir beides ifund casesind schlechte Entscheidungen. Ich würde ein einfaches Array verwenden:

String comments[] = {
    "You look so much better than usual.",
    "Your work is up to its usual standards.",
    "You're quite competent for so little experience."
};

String comment = comments[(int)(Math.random() * 3)];

Als Randnotiz sollten Sie den Multiplikator im Allgemeinen auf der Grundlage der Größe des Arrays berechnen, anstatt das Array hart zu codieren 3.

In Bezug auf , wenn man würde einen Fall / Switch, der Unterschied von einer Kaskade verwendet ifAussagen (oder zumindest ein großer Unterschied) ist , daß switchhalbautomatisch von der Anzahl und die Dichte der Werte Optimierungs basieren kann, während eine Kaskade von ifAnweisungen Blättern der Compiler Sie haben keine andere Wahl, als Code so zu generieren, wie Sie ihn geschrieben haben, und einen Wert nach dem anderen zu testen, bis eine Übereinstimmung gefunden wird. Bei nur drei realen Fällen ist das kaum ein Problem, aber bei einer ausreichenden Anzahl kann / könnte es von Bedeutung sein.

Jerry Sarg
quelle
Dieses Beispiel war übrigens nur ein Beispiel. Wusste aber nicht, dass der Compiler so optimieren kann
TheLQ
6
@ JBRWilkinson. In diesem Fall ist der Wert "out of bounds" nur über einen Compiler-Fehler möglich, für den ich nicht viel Zeit aufwenden möchte (ein Fehler in meinem Code zum Testen des Ergebnisses ist ungefähr so ​​wahrscheinlich wie im generierenden Code). In einer Situation, in der ein außerhalb der Grenzen liegender Wert ein echtes Problem darstellt (z. B. wenn der Index von einem anderen Code empfangen wurde), überprüfe ich die Grenzen zunächst und verwende sie erst nach der Überprüfung als Index.
Jerry Coffin
4
Ich denke, diese Antwort ist sehr spezifisch für das Beispiel, während die Frage allgemeiner war ...
Khelben
3
@Khelben: Es hört sich für mich so an, als hättest du nicht die ganze Antwort gelesen. Der letzte Absatz befasst sich mit den umfassenderen Fragen. Es gibt ein Problem aber: Ich finde sehr wenige Situationen , in denen ich halte entweder eine caseErklärung oder eine Kaskade von ifAussagen geeignet. Meistens sind sie ein (mittelmäßiger) Ersatz für eine Art Karte / Array, und Sie sind besser dran, eine der letzteren direkt zu verwenden.
Jerry Coffin
1
@Titou: Es sei denn, der Compiler ist völlig geistesgestört, das Array wird zur Kompilierungszeit einmal erstellt, und danach verwenden Sie nur noch eine statische Struktur. Wenn Sie dies beispielsweise in C oder C ++ tun, möchten Sie das static constArray zu einem Array machen, um sicherzustellen, dass es immer existiert (in der Frage wurde jedoch keine Sprache angegeben, daher habe ich versucht, auch in der Antwort keine anzunehmen ).
Jerry Coffin
23

Das Problem mit der if...else if...Kette ist, dass ich, wenn ich sie lese, jede einzelne ifBedingung untersuchen muss, um zu verstehen, was das Programm tut. Zum Beispiel könnten Sie so etwas haben:

if (a == 1) {
    // stuff
} else if (a == 2) {
    // stuff
} else if (a == 3) {
    // stuff
} else if (b == 1) {
    // stuff
} else if (b == 2) {
    // stuff
}

(Offensichtlich ist es für eine kleine Anzahl solcher Aussagen nicht so schlimm.)

Ich hätte keine Möglichkeit zu wissen, dass Sie die Bedingungsvariable zur Hälfte geändert haben, ohne jede einzelne Anweisung gelesen zu haben. Da switchSie sich jedoch auf eine einzige Bedingungsvariable beschränken, kann ich auf einen Blick erkennen, was passiert.

Am Ende des Tages würde ich jedoch weder eine switchnoch eine Kette bevorzugen if...else if. Oft ist eine Art Sprungtabelle oder Wörterbuch für Fälle wie in der ursprünglichen Frage oder Polymorphismus eine bessere Lösung (wenn Ihre Sprache dies unterstützt). Das ist natürlich nicht immer möglich, aber ich würde nach einer Lösung suchen, die es switchals ersten Schritt vermeidet ...

Dean Harding
quelle
4
Polymorphismus hat den Nachteil, den Code zu fragmentieren, was es schwieriger macht, ihn zu verstehen, als wenn er sich an einem einzigen Ort befindet. Daher können Sie etwas zögern, bevor Sie sich damit befassen.
Warum ist eine Sprungtabelle / ein Wörterbuch besser als ein Schalter?
Titou
14
switch (which) {
  case 0: comment = "String 1"; break;
  case 1: comment = "String 2"; break;
  case 2: comment = "String 3"; break;
  default: comment = "Oops"; break;
}

Die obige Schreibweise für diese Art von Schaltergehäuse ist ziemlich gebräuchlich. Der Grund, warum Sie den Schalter als sperriger empfanden, war, dass Ihr Körper nur aus einer Zeile bestand und Sie bei einem Schalter auch die break-Anweisung benötigten. Das Schaltergehäuse hatte also die doppelte Körpergröße von if else. Bei umfangreicherem Code trägt die break-Anweisung nicht viel zum body bei. Bei einzeiligen Texten ist es üblich, den Code in dieselbe Zeile wie die case-Anweisung zu schreiben.

Wie andere bereits erwähnt haben, macht ein Schalter die Absicht klarer, Sie möchten eine Entscheidung auf der Grundlage des Werts einer einzelnen Variablen / eines Ausdrucks treffen. Meine Kommentare sind rein aus Sicht der Lesbarkeit und nicht leistungsbezogen.

aufather
quelle
1
Wenn Sie den Schalter in eine Methode einfügen und jeweils returndie richtige Zeichenfolge haben, können Sie die breakAnweisungen entfernen.
Robert Harvey
Ihre Lösung ist auch die schnellste.
Titou
8

In diesem Fall stimmt die switch-Anweisung besser mit der Absicht des Codes überein: Wählen Sie eine Aktion aus, die auf einem einzelnen Wert basiert.

Die if-Anweisungen hingegen sind viel schwerer zu lesen - Sie müssen sich alle ansehen, um sicherzugehen, was los ist. Für mich ist es weniger Code (auch wenn die Zeichenanzahl kann etwas höher sein) , da es weniger ist mental zu analysieren.

FinnNk
quelle
8

Ich stimme Jerry zu, dass ein Array von Zeichenfolgen für diesen speziellen Fall besser ist, aber im Allgemeinen ist es besser, eine switch / case-Anweisung als eine Kette von elseifs zu verwenden. Es ist einfacher zu lesen und manchmal kann der Compiler auf diese Weise besser optimieren, aber es gibt auch einen weiteren Vorteil: Es ist verdammt viel einfacher zu debuggen.

Wenn Sie auf diesen Schalter drücken, müssen Sie nur einmal einen Schritt ausführen, um auf den richtigen Zweig zu gelangen, anstatt vorsichtig mehrere if-Anweisungen nacheinander zu überspringen und möglicherweise die Taste zu schnell zu drücken und darüber hinwegzugehen und etwas zu verpassen und zu haben von vorn anfangen.

Mason Wheeler
quelle
3

Ich bevorzuge Schalter in solchen Fällen, es entspricht viel besser dem Punkt des Codes, führen Sie eine andere Anweisung für jeden anderen Eingabewert. Das if..elsewirkt eher wie ein „Trick“ , um den gleichen Effekt zu erzielen.

switch aussagen sind auch sauberer, es ist einfach, einen Tippfehler in all diesen zu verstecken ==

Bei großen Blöcken in C ist der Wechsel schneller.

else..ifDies kann geeigneter sein, wenn Sie einen Bereich haben (zwischen 1 und 100, tun Sie dies, zwischen 100 und 200, tun Sie das), oder in C, wenn Sie versuchen, mit Elementen wie Zeichenfolgen zu wechseln (dies ist in anderen Sprachen möglich). Welches ist das gleiche.

Ich neige dazu, viele Schalter zu verwenden, wenn ich in C programmiere.

Khelben
quelle
2

Wählen Sie etwas, das effizient und knapp ist, und dokumentieren Sie dann nicht nur, was Sie getan haben, sondern auch, warum.

Code kann überarbeitet werden und nicht immer vom ursprünglichen Autor.

Es gibt Zeiten, in denen Sie eine Implementierung absichtlich einer anderen vorziehen, weil Sie über Code nachdenken, der nicht vorhanden ist.

Walt Steinbrenner
quelle
2

Im Allgemeinen mag ich keinen Ansatz. Long Switch oder If-Anweisungen müssen einfach zu einer objektorientierten Abstraktion umgestaltet werden (jedoch würde ich Ihr Beispiel als kurz, nicht lang einstufen).

Ich würde diese Art von Code persönlich in eine separate Hilfsmethode packen.

private string GetInsult()
{
    int which = (int)(Math.random() * 3);  //  Result is 0, 1, or 2.

    switch (which) {
        case 0: return "You look so much better than usual.";
        case 1: return "Your work is up to its usual standards.";
        case 2: return "You're quite competent for so little experience.";
        default: return "Oops -- something is wrong with this code.";
    }
}

public void Foo()
{
    string comment = GetInsult();
    Print(comment);
}

Wenn Sie den Schalter in einer separaten Methode platzieren, können Sie return-Anweisungen direkt in der switch-Anweisung platzieren (zumindest in c #), sodass auch keine break-Anweisungen erforderlich sind und der Code viel einfacher zu lesen ist.

Und das ist imho viel schöner als der if / else if / else if Ansatz.

Pete
quelle
3
Ich persönlich hasse es, Dinge auf eine andere Art und Weise zu lösen, weil es hässlich aussieht. Wirrwarr Methodenliste und sieht schlimmer aus IMHO. Ich würde dies nur tun, wenn A) der Code irgendwo dupliziert wird oder B) an anderer Stelle nützlich sein könnte
TheLQ
1
Welche Methodenliste? und warum ist Ihre Methodenliste schlechter als Ihr Code? Ich dachte, wir haben die Methode "Alles auf einmal behalten, damit Sie alles auf einmal sehen können" aufgegeben
Sara
@TheLQ Ich stimme Ihnen im Allgemeinen zu, aber in diesem Fall wird das "comment =" in der Tat durch Petes Vorschlag berücksichtigt.
Titou
0

In Python gibt es keine switch-Anweisung, weil if / elif / else nett ist:

a = 5

if a==1:
    print "do this"
elif a == 2:
    print "do that"
elif a == 3:
    print "do the other"
elif 3 < a < 9:
    print "do more"
elif 9 <= a < 15:
    print "do nothing"
else:
    print "say sorry"

Einfach richtig?

Christopher Mahan
quelle
Elifist nur eine if-Anweisung mit wenigen fehlenden Buchstaben. Es ist definitiv mehr eine ifAnweisung als eine switch-Anweisung. Die Tatsache, dass die Python KEINEN Schalter hat, lässt jemanden, der sie hasst (wie mich), denken, dass sie nicht allein sind.
Dan Rosenstark
Die Python-Formatierung funktioniert bei stackoverflow, aber nicht bei programmers.stackexchange.com :(
Christopher Mahan
Sie sollten sie benachrichtigen, metasofern es sich nicht um ein bekanntes Thema handelt. Danke, dass du mir einen Zeugen gegeben hast.
Dan Rosenstark
ya, fand heraus, dass sie es bei meta.programmers.stackexchange.com/questions/308/…
Christopher Mahan
1
@Yar, erinnert mich an meine Wikipedia-Admin-Tage ... Oh Joy. (Bin ich schon komplett vom Thema
Christopher Mahan
0

Eines der Dinge, die den C / C # -Stil switchbesonders nerven, ist das Beharren darauf, dass der caseWert Literale ist. Eine schöne Sache an VB / VB.NET ist, select/casedass jeder Fall ein boolescher Ausdruck sein kann. Das ist praktisch. Insofern eine Reihe sich gegenseitig ausschließender boolescher Ausdrücke oft hilfreich ist, ist eine Reihe von if / else ifs flexibler, ganz zu schweigen davon, dass das Schreiben und Lesen effizienter ist.

Joel Brown
quelle