Devo usare istruzioni switch o long if ... else chains?

36

Spesso quando sento parlare dell'istruzione switch, è rimandata come un modo per sostituire le catene long se ... else. Ma sembra che quando uso l'istruzione switch sto scrivendo altro codice che vorrei scrivere solo se ... altro. Hai anche altri problemi come mantenendo tutte le variabili per tutte le chiamate nello stesso ambito .

Ecco un codice che rappresenta il flusso che normalmente scrivo ( grazie a 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.";
}

Poi vogliono che lo sostituisca con questo:

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

Sembra molto più codice in una sintassi molto più scomoda. Ma c'è davvero un vantaggio nell'usare l'istruzione switch?

    
posta TheLQ 30.10.2010 - 22:26
fonte

10 risposte

57

Per questa particolare situazione, mi sembra che sia if che case siano scelte sbagliate. Userei un array semplice:

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)];

Come nota a margine, dovresti generalmente calcolare il moltiplicatore in base alla dimensione dell'array piuttosto che codificare a fondo il 3 .

Per quanto riguarda quando vorresti utilizzare un caso / switch, la differenza tra una cascata di istruzioni if (o almeno una differenza principale) è che switch può ottimizzare in modo semi-automatico basato sul numero e sulla densità dei valori, mentre una cascata di istruzioni if lascia il compilatore con poca scelta ma per generare codice come lo hai scritto, testando un valore dopo l'altro fino a trovare una corrispondenza. Con solo tre casi reali, non è certo una preoccupazione, ma con un numero sufficiente può / potrebbe essere significativo.

    
risposta data 30.10.2010 - 22:48
fonte
22

Il problema con la catena if...else if... è che quando arrivo a leggerlo, devo esaminare ogni singola condizione di if per capire cosa sta facendo il programma. Ad esempio, potresti avere qualcosa di simile a questo:

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

(ovviamente, per un piccolo numero di affermazioni come questa, non è così male)

Non avrei modo di sapere che hai cambiato la variabile di condizione a metà strada senza leggere ogni singola dichiarazione. Tuttavia, poiché un switch ti limita a una sola variabile di condizione, posso vedere a colpo d'occhio cosa sta succedendo.

Alla fine della giornata, però, preferirei nessuno dei due switch o una catena di if...else if . Spesso una soluzione migliore è una sorta di tabella di salto o dizionario per casi come nella domanda originale o polimorfismo (se la lingua lo supporta). Naturalmente non è sempre possibile, ma cercherò una soluzione che eviti switch come primo passo ...

    
risposta data 31.10.2010 - 01:56
fonte
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;
}

Il modo sopra descritto di scrivere questo tipo di case degli interruttori è abbastanza comune. Il motivo per cui hai sentito il caso di interruttore se più ingombrante è perché il tuo corpo era solo una linea e con un caso di commutazione hai anche bisogno dell'istruzione break. Quindi il caso del cambio aveva il doppio delle dimensioni del corpo di if else. Con un codice più sostanziale, la frase break non aggiungerà molto al corpo. Per il corpo a linea singola, è pratica comune scrivere il codice nella stessa riga dell'istruzione case.

Come altri hanno già menzionato, un caso di commutazione rende l'intento più chiaro, si desidera prendere una decisione in base al valore di una singola variabile / espressione. I miei commenti sono puramente dal punto di vista della leggibilità e non basati sulle prestazioni.

    
risposta data 31.10.2010 - 04:34
fonte
8

In questo caso l'istruzione switch corrisponde più chiaramente allo scopo del codice: scegli un'azione da eseguire in base a un singolo valore.

Le dichiarazioni if invece sono molto più difficili da leggere - devi guardarle tutte per essere sicuri di cosa sta succedendo. Per me è meno codice (anche se il numero di caratteri potrebbe essere leggermente più alto) perché c'è meno da analizzare mentalmente.

    
risposta data 30.10.2010 - 22:32
fonte
8

Sono d'accordo con Jerry sul fatto che una serie di stringhe sia migliore per questo caso particolare, ma che in generale è meglio usare un'istruzione switch / case piuttosto che una catena di elseifs. È più facile da leggere e talvolta il compilatore può fare un miglior lavoro di ottimizzazione in questo modo, ma c'è anche un altro vantaggio: è molto più facile eseguire il debug.

Quando colpisci quell'interruttore, devi solo passare una volta per finire sulla branca destra, invece di esaminare attentamente più istruzioni if una alla volta, e possibilmente colpire la chiave troppo velocemente e passarla oltre e mancare qualcosa e dover ricominciare da capo.

    
risposta data 30.10.2010 - 23:21
fonte
3

Preferisco passare in questo tipo di casi, corrisponde molto meglio al punto del codice, eseguire una dichiarazione diversa per ogni diverso valore di input. if..else si comporta più come un "trucco" per ottenere lo stesso effetto.

Anche le dichiarazioni

switch sono più pulite, è facile avere un errore di battitura nascosto in tutti quei ==

Inoltre, per i blocchi grandi in C, lo switch è più veloce.

else..if può essere più appropriato quando hai qualcosa come range (tra 1 e 100, fai questo, tra 100 e 200 farlo), o in C, quando provi a fare switch con elementi come stringhe (cioè possibile in altre lingue). Che è lo stesso.

Tendo ad usare molti switch quando programmo in C.

    
risposta data 02.11.2010 - 17:11
fonte
2

Scegli qualcosa che sia efficiente, conciso e poi document non solo quello che hai fatto, ma perché.

Il codice può essere rivisitato, e non sempre dal suo autore originale.

Ci sono momenti in cui potresti deliberatamente scegliere un'implementazione rispetto a un'altra perché stai pensando al codice che non esiste.

    
risposta data 02.11.2010 - 17:00
fonte
2

In genere non mi piace nessuno dei due approcci. Interruttore lungo o se le istruzioni semplicemente implorano di essere refactored ad un'astrazione orientata agli oggetti (tuttavia il tuo esempio lo classificherei breve, non lungo).

Vorrei avvolgere personalmente quel tipo di codice in un metodo di supporto separato.

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);
}

L'inserimento dello switch in un metodo separato consente di inserire istruzioni di ritorno direttamente all'interno dell'istruzione switch (almeno in c #), eliminando anche la necessità di istruzioni di interruzione, rendendo il codice molto più facile da leggere.

E questo è molto più bello dell'approccio if / else if / else se.

    
risposta data 03.01.2012 - 10:08
fonte
0

In python, non c'è alcuna istruzione switch, perché if / elif / else è bello:

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"

Semplice vero?

    
risposta data 02.11.2010 - 17:18
fonte
0

Una delle cose che rende particolarmente fastidioso lo stile di% C_C% in% co_de è l'insistenza sul fatto che il valore di switch sia letterale. Una cosa carina di VB / VB.NET è che case consente a ciascun caso di essere qualsiasi espressione booleana. Che è conveniente. Nella misura in cui una serie di espressioni booleane mutuamente esclusive è spesso utile, una serie di if / else ifs è più flessibile, per non parlare dell'essere più efficiente da digitare e leggere.

    
risposta data 02.01.2012 - 23:07
fonte

Leggi altre domande sui tag