Dovrei usare le istruzioni switch o lungo se ... altrimenti catene?


36

Spesso quando sento parlare dell'istruzione switch, viene rimandato come un modo per sostituire lunghe catene se ... altro. Ma sembra che quando uso l'istruzione switch sto scrivendo più codice che scriverei solo se ... altro. Hai anche altri problemi come mantenere tutte le variabili per tutte le chiamate nello stesso ambito .

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

Quindi 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ù imbarazzante. Ma c'è davvero un vantaggio nell'usare l'istruzione switch?


Ugh. Sì, è sicuramente più voluminoso, ma solo nella famiglia C, perché la sua sintassi per le dichiarazioni dei casi è così brutta.
Mason Wheeler,


1
Dovresti evitare entrambi quando possibile. È molto meglio creare una struttura di dati ed eseguire una ricerca, anche se la destinazione di ricerca è una funzione o una classe.
Kevin Cline il

lo switch è più veloce, almeno in .net. Non so di Java.
Knerd,

Può essere rifattorizzato in un dizionario di casi e metodi e uno "if"
Pavel Yermalovich,

Risposte:


57

Per questa particolare situazione, mi sembra che entrambi ife casesiano 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 a codificare 3.

Quanto a quando useresti un case / switch, la differenza da una cascata di ifistruzioni (o almeno una differenza maggiore) è che switchpuò ottimizzare semi-automaticamente in base al numero e alla densità dei valori, mentre una cascata di ifistruzioni lascia il compilatore con poca scelta ma generare codice mentre lo hai scritto, testando un valore dopo l'altro fino a trovare una corrispondenza. Con solo tre casi reali, non è certo un problema, ma con un numero sufficiente può / potrebbe essere significativo.


Quell'esempio era solo un esempio BTW. Non sapevo però che il compilatore potesse ottimizzare in quel modo
TheLQ

6
@JBRWilkinson. In questo caso, il valore fuori limite è possibile solo tramite un bug del compilatore, sul quale non sono incline a dedicare molto tempo (un bug nel mio codice per testare il risultato è più probabile che nel codice di generazione). In una situazione in cui un valore fuori limite era una vera preoccupazione (ad esempio, l'indice veniva ricevuto da un altro codice), controllavo prima i limiti e lo usavo come indice solo dopo il controllo.
Jerry Coffin,

4
Penso che questa risposta sia molto specifica sull'esempio, mentre la domanda era più generica di così ...
Khelben

3
@Khelben: mi sembra che non ti sia disturbato a leggere l'intera risposta. L'ultimo paragrafo affronta le questioni più ampie. C'è un problema però: ho trovato molto poche le situazioni in cui ritengo sia una casedichiarazione o di una cascata di ifdichiarazioni idonee. Il più delle volte, sono un sostituto (mediocre) di una sorta di tipo di cosa mappa / matrice e stai meglio usando direttamente uno di questi ultimi.
Jerry Coffin,

1
@Titou: a meno che il compilatore non sia del tutto risolto, l'array verrà creato una volta al momento della compilazione, dopodiché stai solo usando una struttura statica. Ad esempio, se lo facessi in C o C ++, vorresti renderlo un static constarray, per assicurarti che esistesse sempre (ma non è stato fornito alcun linguaggio nella domanda, quindi ho cercato di non assumerne nemmeno uno nella risposta ).
Jerry Coffin,

23

Il problema con la if...else if...catena è che quando vengo a leggerlo, devo esaminare ogni singola ifcondizione per capire cosa sta facendo il programma. Ad esempio, potresti avere qualcosa del genere:

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 è poi così male)

Non avrei modo di sapere che hai modificato la variabile di condizione a metà strada senza leggere ogni singola istruzione. Tuttavia, poiché switchti limita a una singola variabile di condizione, posso vedere a colpo d'occhio cosa sta succedendo.

Alla fine, però, preferirei né switchuna né 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 tua lingua lo supporta). Naturalmente non è sempre possibile, ma cercherò una soluzione che eviti switchcome primo passo ...


4
il polimorfismo ha lo svantaggio di frammentare il codice, rendendo più difficile la comprensione rispetto a trovarsi in una singola posizione. Quindi potresti esitare un po 'prima di cambiarlo.

Perché una tabella / dizionario di salto è meglio di uno switch?
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;
}

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

Come altri hanno già accennato, un caso switch rende più chiaro l'intento, vuoi 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.


1
Se si inserisce l'opzione in un metodo e si dispone di ogni caso returnla stringa corretta, è possibile eliminare le breakistruzioni.
Robert Harvey,

La tua soluzione è anche la più veloce.
Titou,

8

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

Le dichiarazioni if ​​d'altra parte sono molto più difficili da leggere: devi guardarle tutte per essere sicuro di cosa sta succedendo. Per me è meno codice (anche se il conteggio dei personaggi potrebbe essere leggermente più alto) in quanto c'è meno da analizzare mentalmente.


8

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

Quando premi questo interruttore, devi solo fare un passo una volta per finire sul ramo giusto, invece di scavalcare attentamente diverse istruzioni if ​​una alla volta, e possibilmente premere la chiave troppo velocemente e superarla e perdere qualcosa e avere ricominciare da capo.


3

Preferisco passare a questo tipo di casi, corrisponde molto meglio al punto del codice, esegue un'istruzione diversa per ogni diverso valore di input. Si if..elsecomporta più come un "trucco" per ottenere lo stesso effetto.

switch le dichiarazioni sono anche più pulite, è facile avere un refuso nascosto in tutte quelle ==

Inoltre, per blocchi di grandi dimensioni in C, l'interruttore è più veloce.

else..ifpuò essere più appropriato quando hai qualcosa come intervalli (tra 1 e 100, fai questo, tra 100 e 200 fallo), o in C, quando provi a cambiare con elementi come stringhe (che è possibile in altre lingue). Che è lo stesso

Tendo a usare molti switch quando programmo in C.


2

Scegli qualcosa che sia efficiente, conciso e quindi documenta non solo ciò che hai fatto, ma perché.

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

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


2

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

Avrei personalmente avvolto 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);
}

Posizionare lo switch in un metodo separato consente di posizionare le istruzioni return direttamente all'interno dell'istruzione switch (almeno in c #), eliminando la necessità di istruzioni break, rendendo il codice molto più facile da leggere.

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


3
Personalmente odio il modo di "metterlo in un altro metodo perché sembra brutto" per risolvere le cose. Elenco dei metodi di disordine e aspetto peggiore IMHO. Lo farei solo se A) il codice fosse duplicato da qualche parte o B) Potrebbe essere utile altrove
TheLQ

1
quale elenco di metodi? e perché il tuo elenco di metodi viene ingombrato peggio del tuo codice? Pensavo che avessimo superato l'età "mantieni tutto in un unico metodo in modo da poter vedere tutto in una volta"
sara,

@TheLQ Sono d'accordo con te in generale, ma in questo caso il "comment =" è effettivamente preso in considerazione dalla proposta di Pete.
Titou,

0

In python, non esiste alcuna istruzione switch, perché if / elif / else è utile:

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?


Elifè solo un'istruzione if con alcune lettere mancanti. È sicuramente più simile a ifun'istruzione che a un'istruzione switch. Il fatto che Python NON abbia un interruttore fa pensare a chi li odia (come me) di non essere solo.
Dan Rosenstark,

La formattazione di Python funziona su StackOverflow ma non su programmers.stackexchange.com :(
Christopher Mahan

Dovresti avvisarli a metameno che non sia un argomento noto. Grazie per avermi dato un testimone.
Dan Rosenstark,


1
@Yar, mi ricorda i miei giorni di amministrazione di Wikipedia ... Oh Joy. (Sono ancora completamente fuori tema?)
Christopher Mahan

0

Una delle cose che rende switchparticolarmente fastidioso lo stile C / C # è l'insistenza sul casevalore letterale. Una cosa bella di VB / VB.NET è che select/caseconsente a ogni caso di essere qualsiasi espressione booleana. È conveniente. Nella misura in cui una serie di espressioni booleane reciprocamente esclusive è spesso utile, una serie di if / else ifs è più flessibile, per non parlare della maggiore efficienza nella digitazione e nella lettura.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.