Devrais-je utiliser des instructions switch ou long si… sinon des chaînes?

36

Souvent, lorsque j'entends parler de la déclaration switch, elle est retardée comme moyen de remplacer les chaînes long if ... else. Mais il semble que lorsque j'utilise l'instruction switch, j'écris plus de code que je ne ferais qu'écrire si ... sinon. Vous avez également d'autres problèmes, tels que le maintien de toutes les variables pour tous les appels dans le même périmètre .

Voici un code qui représente le flux que j'écris normalement ( grâce à 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.";
}

Ensuite, ils veulent que je remplace cela par ceci:

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

On dirait beaucoup plus de code dans une syntaxe beaucoup plus gênante. Mais y a-t-il vraiment un avantage à utiliser l'instruction switch?

TheLQ
la source
Pouah. Oui, c'est certainement plus volumineux, mais seulement dans la famille C, car sa syntaxe pour les instructions de cas est si moche.
Mason Wheeler
5
Ce genre de chose a beaucoup été discuté sur stackoverflow: - stackoverflow.com/questions/449273/… - stackoverflow.com/questions/767821/… - stackoverflow.com/questions/97987/switch-vs-if-else
Yevgeniy Brikman
1
Vous devriez éviter les deux autant que possible. Il est bien préférable de créer une structure de données et d'effectuer une recherche, même si la cible de recherche est une fonction ou une classe.
kevin cline
le commutateur est plus rapide, au moins en .net. Je ne sais pas à propos de Java.
Knerd
Peut être refondu en un dictionnaire de cas et méthodes et un "si"
Pavel Yermalovich

Réponses:

57

Pour cette situation, il me semble que les deux ifet casesont de mauvais choix. J'utiliserais un tableau simple:

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

En remarque, vous devez généralement calculer le multiplicateur en fonction de la taille du tableau plutôt que de le coder en dur 3.

Quant à savoir quand vous souhaitez utiliser un cas / commutateur, la différence par rapport à une cascade de ifdéclarations (ou au moins une différence majeure) est que switchpeut optimize semi-automatique en fonction du nombre et de la densité des valeurs, alors qu'une cascade de ifdéclarations feuilles du compilateur sans autre choix que de générer le code tel que vous l'avez écrit, en testant une valeur après l'autre jusqu'à ce qu'il trouve une correspondance. Avec seulement trois cas réels, ce n’est pas une préoccupation, mais avec un nombre suffisant, cela peut / pourrait être important.

Jerry Coffin
la source
Cet exemple était seulement un exemple BTW. Je ne savais pas que le compilateur peut optimiser comme ça
TheLQ
6
@ JBRWilkinson. Dans ce cas, la valeur hors limites n'est possible que via un bogue du compilateur, sur lequel je ne suis pas enclin à passer beaucoup de temps (un bogue dans mon code pour tester le résultat est à peu près aussi probable que dans le code générateur). Dans une situation où une valeur hors limites représentait un réel problème (par exemple, l'index était reçu d'un autre code), je vérifiais simplement les limites en premier et ne l'utilisais comme indice qu'après la vérification.
Jerry Coffin
4
Je pense que cette réponse est très précise à propos de l'exemple, alors que la question était plus générique que cela ...
Khelben
3
@Khelben: il me semble que vous n'avez pas pris la peine de lire toute la réponse. Le dernier paragraphe aborde les questions plus larges. Il y a cependant un problème: je trouve très peu de situations dans lesquelles je considère soit une casedéclaration, soit une cascade d' ifénoncés appropriés. La plupart du temps, ils sont un substitut (médiocre) d'une sorte de carte / type de tableau, et vous feriez mieux d'utiliser l'un de ces derniers directement.
Jerry Coffin
1
@ Titou: à moins que le compilateur soit complètement braindead, le tableau sera construit une fois au moment de la compilation, après quoi vous utiliserez une structure statique. Par exemple, si vous faisiez cela en C ou C ++, vous voudriez en faire un static consttableau, pour vous assurer qu'il a toujours existé (mais aucun langage n'a été donné dans la question, j'ai donc essayé de ne pas en assumer un dans la réponse )
Jerry Coffin
23

Le problème avec la if...else if...chaîne, c'est que lorsque je viens la lire, je dois examiner chaque ifcondition pour comprendre ce que fait le programme. Par exemple, vous pourriez avoir quelque chose comme ceci:

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

(évidemment, pour un petit nombre d'énoncés comme celui-ci, ce n'est pas si grave)

Je n'aurais aucun moyen de savoir que vous avez modifié la variable de condition à mi-chemin sans lire chaque déclaration. Cependant, comme une switchlimite vous limite à une seule variable de condition, je peux voir d'un coup d'œil ce qui se passe.

En fin de compte, cependant, je préférerais ni l' switchun ni l'autre if...else if. Une meilleure solution est souvent une sorte de table de saut ou de dictionnaire pour des cas comme dans la question initiale, ou un polymorphisme (si votre langage le permet). Ce n'est pas toujours possible, bien sûr, mais je chercherais une solution qui évite switchdans un premier temps ...

Dean Harding
la source
4
Le polymorphisme présente l'inconvénient de fragmenter le code, ce qui le rend plus difficile à comprendre que de se trouver à un seul endroit. Par conséquent, vous pouvez hésiter quelque peu avant de passer à cela.
Pourquoi une table de saut / dictionnaire est-il meilleur qu'un commutateur?
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;
}

La manière ci-dessus d'écrire ce type de boîtier de commutateur est assez commune. Si vous avez l'impression que le boîtier de commutateur est plus volumineux, c'est parce que votre corps ne comptait qu'une seule ligne et qu'avec un boîtier de commutateur, vous aviez également besoin de l'instruction break. Donc, le boîtier de l'interrupteur avait deux fois la taille du corps de if else. Avec un code plus substantiel, l'instruction break n'ajoutera pas grand chose au corps. Pour un corps de ligne unique, il est courant d'écrire le code sur la même ligne que l'instruction case.

Comme d'autres l'ont déjà mentionné, un cas de commutation clarifie l'intention, vous voulez prendre une décision en fonction de la valeur d'une variable / expression unique. Mes commentaires sont purement du point de vue de la lisibilité et non de la performance.

aufather
la source
1
Si vous placez le commutateur dans une méthode et que vous avez la casse returnappropriée dans chaque cas , vous pouvez éliminer les breakinstructions.
Robert Harvey
Votre solution est aussi la plus rapide.
Titou
8

Dans ce cas, l'instruction switch correspond plus clairement à l'intention du code: choisissez une action à entreprendre en fonction d'une valeur unique.

Les déclarations if, en revanche, sont beaucoup plus difficiles à lire - vous devez les examiner toutes pour être sûr de ce qui se passe. Pour moi, c'est moins de code (même si le nombre de caractères peut être légèrement supérieur), car il y a moins d'analyse mentale.

FinnNk
la source
8

Je conviens avec Jerry qu'un tableau de chaînes est préférable pour ce cas particulier, mais qu'en général, il est préférable d'utiliser une instruction switch / case plutôt qu'une chaîne d'alternatifs. C'est plus facile à lire, et parfois le compilateur peut mieux optimiser cette façon, mais il y a aussi un autre avantage: c'est vraiment beaucoup plus facile à déboguer.

Lorsque vous appuyez sur cette touche, vous n’avez qu’une étape à franchir pour vous retrouver sur la branche droite, au lieu de passer avec précaution sur plusieurs déclarations if, une sur une, recommencer à zéro.

Maçon Wheeler
la source
3

Je préfère changer dans ce genre de cas, cela correspond beaucoup mieux au point du code, exécute une instruction différente pour chaque valeur d'entrée différente. Le if..elseagit plutôt comme un "truc" pour obtenir le même effet.

switch les déclarations sont également plus propres, il est facile d'avoir une faute de frappe cachée dans tous ceux ==

De plus, pour les gros blocs en C, le changement est plus rapide.

else..ifpeut être plus approprié lorsque vous avez quelque chose comme les plages (entre 1 et 100, faites ceci, entre 100 et 200 faites cela), ou en C, lorsque vous essayez de faire basculer avec des éléments tels que des chaînes (ce qui est possible dans d'autres langues). Qui est un même.

J'ai tendance à utiliser beaucoup de commutateurs lorsque je programme en C.

Khelben
la source
2

Choisissez quelque chose qui est efficace, concis, puis documentez non seulement ce que vous avez fait, mais pourquoi.

Le code peut être revisité, et pas toujours par son auteur original.

Il peut arriver que vous choisissiez délibérément une implémentation plutôt qu'une autre parce que vous envisagez le code qui n'existe pas.

Walt Stoneburner
la source
2

Je n'aime généralement pas l'une ou l'autre approche. Interrupteur long ou si les instructions ne demandent qu'à être refactorisées en une abstraction orientée objet (cependant, je classerais votre exemple comme étant court, pas long).

Personnellement, j'encapsulerais ce genre de code dans une méthode d'assistance distincte.

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

Le fait de placer le commutateur dans une méthode distincte vous permet de placer des instructions return directement dans celle-ci (au moins en c #), ce qui élimine le besoin d'instructions break et simplifie la lecture du code.

Et c’est beaucoup mieux que l’approche if / else if / else if.

Pete
la source
3
Personnellement, je déteste le "mettre dans une autre méthode parce qu'il a l'air moche" façon de résoudre les problèmes. Liste des méthodes de clutter et semble pire à mon humble avis. Je ne le ferais que si A) le code est dupliqué quelque part ou B) pourrait être utile ailleurs
TheLQ
1
quelle liste de méthodes? et pourquoi votre liste de méthodes est-elle encombrée de manière pire que votre code est encombré? Je pensais que nous avions dépassé l'âge "garder tout en une seule méthode pour pouvoir tout voir en même temps"
sara
@TheLQ Je suis d'accord avec vous en général, mais dans ce cas même, le "comment =" est effectivement pris en compte par la proposition de Pete.
Titou
0

En python, il n'y a pas d'instruction switch, car if / elif / else est sympa:

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"

Simple droit?

Christopher Mahan
la source
Elifest juste une déclaration if avec quelques lettres manquantes. Cela ressemble certainement plus à une ifdéclaration qu'à une déclaration switch. Le fait que le Python n'ait PAS de commutateur fait en sorte que ceux qui les détestent (comme moi) pensent qu'ils ne sont pas seuls.
Dan Rosenstark
Le formatage Python fonctionne avec stackoverflow mais pas avec programmers.stackexchange.com :(
Christopher Mahan
Vous devriez les alerter à metamoins que ce ne soit un sujet connu. Merci de m'avoir donné un témoin.
Dan Rosenstark le
ya, découvert qu'ils l'ont en cours à meta.programmers.stackexchange.com/questions/308/…
Christopher Mahan
1
@Yar, ça me rappelle mes jours d'administration sur wikipedia ... Oh Joy. (suis-je encore complètement hors-sujet?)
Christopher Mahan
0

Une des choses qui rend le style C / C # switchparticulièrement ennuyeux est l’insistance sur le fait que la casevaleur est littérale. Une bonne chose à propos de VB / VB.NET est que cela select/casepermet à chaque cas d’être une expression booléenne. C'est pratique. Dans la mesure où une série d'expressions booléennes mutuellement exclusives est souvent utile, une série de if / else if est plus flexible, sans oublier d'être plus efficace pour taper et lire.

Joel Brown
la source