¿Debo usar declaraciones de cambio o largas si ... si no cadenas?

36

A menudo, cuando escucho sobre la declaración de cambio, se pospone como una forma de reemplazar largas cadenas ... si no. Pero parece que cuando uso la instrucción switch estoy escribiendo más código del que estaría escribiendo si ... de lo contrario. También tiene otros problemas, como mantener todas las variables para todas las llamadas en el mismo ámbito .

Aquí hay un código que representa el flujo que normalmente escribo ( gracias 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.";
}

Luego quieren que reemplace eso con esto:

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

Parece mucho más código en una sintaxis mucho más incómoda. Pero, ¿hay realmente una ventaja al usar la instrucción switch?

TheLQ
fuente
Ugh Sí, eso es ciertamente más voluminoso, pero solo en la familia C, porque su sintaxis para las declaraciones de casos es muy fea.
Mason Wheeler
55
Este tipo de cosas se ha discutido mucho en stackoverflow: - stackoverflow.com/questions/449273/… - stackoverflow.com/questions/767821/… - stackoverflow.com/questions/97987/switch-vs-if-else
Yevgeniy Brikman
1
Debe evitar ambos siempre que sea posible. Es mucho mejor crear una estructura de datos y realizar una búsqueda, incluso si el objetivo de búsqueda es una función o clase.
Kevin Cline
El cambio es más rápido, al menos en .net. No se sobre Java.
Knerd
Se puede refactorizar a un diccionario de casos y métodos y un "si"
Pavel Yermalovich

Respuestas:

57

Para esta situación particular, me parece que ambas ify caseson malas elecciones. Yo usaría una matriz 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)];

Como nota al margen, generalmente debe calcular el multiplicador en función del tamaño de la matriz en lugar de codificarlo 3.

En cuanto a cuando se le utilice un caso / interruptor, la diferencia de una cascada de ifdeclaraciones (o al menos una gran diferencia) es que switchpuede semiautomática optimización basándose en el número y la densidad de los valores, mientras que una cascada de ifdeclaraciones hojas del compilador con pocas opciones más que generar el código tal como lo has escrito, probando un valor tras otro hasta que encuentre una coincidencia. Con solo tres casos reales, eso no es una preocupación, pero con un número suficiente puede / podría ser significativo.

Jerry Coffin
fuente
Ese ejemplo fue solo un ejemplo, por cierto. Sin embargo, no sabía que el compilador puede optimizar así
TheLQ
66
@JBRWilkinson. En este caso, el valor fuera de límites solo es posible a través de un error del compilador, en el que no estoy dispuesto a pasar mucho tiempo (un error en mi código para probar el resultado es tan probable como en el código generador). En una situación en la que un valor fuera de límites era una preocupación real (por ejemplo, el índice se estaba recibiendo de otro código), primero verificaría los límites y lo usaría como índice solo después de la verificación.
Jerry Coffin
44
Creo que esta respuesta es muy específica sobre el ejemplo, mientras que la pregunta era más genérica que eso ...
Khelben
3
@Khelben: me parece que no te molestaste en leer la respuesta completa. El último párrafo aborda los temas más amplios. Hay un problema, sin embargo: Me parece muy pocas situaciones en las que considero ya sea una casedeclaración o una cascada de ifdeclaraciones adecuadas. La mayoría de las veces, son un sustituto (mediocre) de algún tipo de mapa / matriz, y es mejor usar uno de estos últimos directamente.
Jerry Coffin el
1
@Titou: a menos que el compilador esté completamente loco, la matriz se construirá una vez en el momento de la compilación, y después de eso solo usará una estructura estática. Por ejemplo, si estaba haciendo esto en C o C ++, desearía convertirlo en una static constmatriz, para asegurarse de que siempre existió (pero no se proporcionó lenguaje en la pregunta, por lo que traté de no asumir ninguno en la respuesta )
Jerry Coffin
23

El problema con la if...else if...cadena es que cuando vengo a leerlo, tengo que mirar cada ifcondición para entender lo que está haciendo el programa. Por ejemplo, podría tener algo como esto:

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

(obviamente, para un pequeño número de declaraciones como esta, no es tan malo)

No tendría forma de saber que cambiaste la variable de condición a la mitad sin leer cada declaración. Sin embargo, debido a que lo switchlimita a una sola variable de condición, puedo ver de un vistazo lo que está sucediendo.

Al final del día, sin embargo, preferiría ninguno switcho una cadena de if...else if. A menudo, una mejor solución es algún tipo de tabla de salto o diccionario para casos como en la pregunta original, o polimorfismo (si su idioma lo admite). No siempre es posible, por supuesto, pero buscaría una solución que evite switchcomo primer paso ...

Dean Harding
fuente
44
El polimorfismo tiene la desventaja de fragmentar el código, lo que hace que sea más difícil de entender en comparación con estar en una sola ubicación. Por lo tanto, puede dudar un poco antes de cambiar a eso.
¿Por qué una mesa / diccionario de salto es mejor que un interruptor?
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 forma anterior de escribir este tipo de mayúsculas y minúsculas es bastante común. La razón por la que sintió la caja del interruptor si es más voluminosa es porque su cuerpo era solo una línea y con una caja del interruptor también necesitaba la declaración de interrupción. Por lo tanto, la caja del interruptor tenía el doble del tamaño del cuerpo de lo contrario. Con un código más sustancial, la declaración de ruptura no agregará mucho al cuerpo. Para el cuerpo de una sola línea, es una práctica común escribir el código en la misma línea que la declaración del caso.

Como otros ya han mencionado, un caso de cambio aclara la intención, desea tomar una decisión basada en el valor de una sola variable / expresión. Mis comentarios son puramente desde un punto de vista de legibilidad y no se basan en el rendimiento.

aufather
fuente
1
Si coloca el interruptor en un método y tiene cada caso returnla cadena adecuada, puede eliminar las breakdeclaraciones.
Robert Harvey
Tu solución también es la más rápida.
Titou
8

En este caso, la declaración de cambio coincide más claramente con la intención del código: elija una acción para realizar en función de un solo valor.

Las declaraciones if, por otro lado, son mucho más difíciles de leer: debe verlas todas para asegurarse de lo que está sucediendo. Para mí es menos código (incluso si el recuento de caracteres puede ser un poco más alto) ya que hay menos para analizar mentalmente.

FinnNk
fuente
8

Estoy de acuerdo con Jerry en que una serie de cadenas es mejor para este caso en particular, pero que en general es mejor usar una declaración de cambio / caso que una cadena de otras cosas. Es más fácil de leer y, a veces, el compilador puede hacer un mejor trabajo de optimización de esa manera, pero también hay otro beneficio: es muchísimo más fácil de depurar.

Cuando presiona ese interruptor, solo tiene que dar un paso una vez para terminar en la rama correcta, en lugar de pasar con cuidado por varias declaraciones if una a la vez, y posiblemente presionar la tecla demasiado rápido y pasar por alto y perder algo y tener comenzar de nuevo.

Mason Wheeler
fuente
3

Prefiero cambiar en ese tipo de casos, coincide mucho mejor con el punto del código, ejecutar una declaración diferente para cada valor de entrada diferente. El if..elseactúa más como un "truco" para lograr el mismo efecto.

switch las declaraciones también son más limpias, es fácil tener un error tipográfico oculto en todos esos ==

Además, para bloques grandes en C, el cambio es más rápido.

else..ifpuede ser más apropiado cuando tienes algo como rangos (entre 1 y 100, haz esto, entre 100 y 200 haz eso), o en C, cuando intentas hacer un cambio con elementos como cadenas (eso es posible en otros idiomas). Que es lo mismo.

Tiendo a usar muchos interruptores cuando programo en C.

Khelben
fuente
2

Elija algo que sea eficiente, conciso, y luego documente no solo lo que ha hecho, sino también por qué.

El código puede ser revisado, y no siempre por su autor original.

Hay momentos en los que puede elegir deliberadamente una implementación sobre otra porque está pensando en el código que no existe.

Walt Stoneburner
fuente
2

Generalmente no me gusta ninguno de los enfoques. Cambio largo o si las declaraciones solo piden ser refactorizadas a una abstracción orientada a objetos (sin embargo, su ejemplo lo clasificaría como corto, no largo).

Personalmente, envolvería ese tipo de código en un método auxiliar separado.

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

Colocar el interruptor en un método separado le permite colocar declaraciones de retorno directamente dentro de la declaración del interruptor (al menos en c #), eliminando la necesidad de declaraciones de interrupción, lo que hace que el código sea mucho más fácil de leer.

Y esto es mucho mejor que el enfoque if / else if / else if.

Pete
fuente
3
Personalmente odio la forma de "ponerlo en otro método porque se ve feo" para resolver las cosas. Lista de métodos de desorden y se ve peor en mi humilde opinión. Solo haría esto si A) el código está duplicado en alguna parte o B) podría ser útil en otro lugar
TheLQ
1
¿Qué lista de métodos? ¿y por qué su lista de métodos está abarrotada peor que su código está abarrotado? Pensé que habíamos pasado la edad de "mantener todo en un solo método para que puedas ver todo de una vez"
sara
@TheLQ Estoy de acuerdo con usted en general, pero en este mismo caso el "comentario =" está realmente incluido en la propuesta de Pete.
Titou
0

En python, no hay una declaración de cambio, porque si / elif / else es bueno:

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 derecho?

Christopher Mahan
fuente
Elifes solo una declaración if con algunas letras faltantes. Definitivamente es más como una ifdeclaración que una declaración de cambio. El hecho de que Python NO tenga un interruptor hace que alguien que los odia (como yo) piense que no están solos.
Dan Rosenstark
El formateo de Python funciona en stackoverflow pero no en programmers.stackexchange.com :(
Christopher Mahan
Debe alertarlos a metamenos que sea un tema conocido. Gracias por darme un testigo.
Dan Rosenstark
sí, descubrí que lo tienen en meta.programmers.stackexchange.com/questions/308/…
Christopher Mahan
1
@Yar, me recuerda a mis días de administración de Wikipedia ... Oh, alegría. (¿Estoy completamente fuera de tema todavía?)
Christopher Mahan
0

Una de las cosas que hace que el estilo C / C # sea switchparticularmente molesto es la insistencia en que el casevalor sea literal. Una cosa buena de VB / VB.NET es que select/casepermite que cada caso sea una expresión booleana. Eso es conveniente En la medida en que una serie de expresiones booleanas mutuamente excluyentes a menudo es útil, una serie de if / else ifs es más flexible, sin mencionar que es más eficiente para escribir y leer.

Joel Brown
fuente