c++ - tag - ¿Por qué cambiar/Caso y no If/Else If?
if php varias condiciones (15)
Bueno, una de las razones es la claridad ...
si tiene un interruptor / caja, entonces la expresión no puede cambiar ... es decir
switch (foo[bar][baz]) {
case ''a'':
...
break;
case ''b'':
...
break;
}
mientras que con if / else, si escribe por error (o intento):
if (foo[bar][baz] == ''a'') {
....
}
else if (foo[bar][baz+1] == ''b'') {
....
}
las personas que lean tu código se preguntarán "¿se supone que las expresiones foo son las mismas" o "por qué son diferentes"?
Esta pregunta apunta principalmente a C / C ++, pero creo que otros idiomas también son relevantes.
No puedo entender por qué se sigue utilizando el conmutador / caja en lugar de if / else si. Me parece mucho a usar goto''s, y resulta en el mismo tipo de código desordenado, mientras que los mismos resultados podrían lograrse con if / else si es de una manera mucho más organizada.
Aún así, veo estos bloques con bastante frecuencia. Un lugar común para encontrarlos está cerca de un bucle de mensaje (WndProc ...), mientras que estos son algunos de los lugares en los que se producen los mayores estragos: las variables se comparten a lo largo de todo el bloque, incluso cuando no son apropiadas (y no pueden ser inicializado dentro de él). Se debe poner especial atención en no dejar caer las pausas, y así sucesivamente ...
Personalmente, evito usarlos, y me pregunto si me estoy perdiendo algo.
¿Son más eficientes que los de if / else? ¿Son llevados por la tradición?
Claridad. Como dije here , una pista de que else if
es problemático es
la frecuencia con la que ELSE IF se utiliza de una manera mucho más limitada de lo que permite la sintaxis. Es un martillo de flexibilidad, que permite que se prueben condiciones totalmente no relacionadas. Pero se usa rutinariamente para atravesar las moscas de CASE, comparando la misma expresión con valores alternativos ...
Esto reduce la legibilidad del código. Dado que la estructura permite un universo de complejidad condicional, el lector debe tener más en cuenta al analizar ELSE IF que al analizar CASE.
En realidad, una declaración de cambio implica que estás trabajando en algo que es más o menos una enumeración que te da una pista instantánea de lo que está sucediendo.
Dicho esto, un cambio en una enumeración en cualquier idioma de OO podría codificarse mejor, y una serie de if / else en el mismo valor de estilo "enum" sería al menos tan mala o incluso peor para transmitir el significado.
Estoy bastante seguro de que compilan las mismas cosas que if
/ else if
, pero encuentro que el switch
/ case
más fácil de leer cuando hay más de 2 o 3 else
s.
La caja del interruptor se usa principalmente para tener la opción de hacer en la programación. Esto no está relacionado con la declaración condicional como:
si su programa solo requiere la opción de hacer, entonces por qué usa el bloque if / else e incrementa el esfuerzo de programación además de reducir la velocidad de ejecución del programa.
La razón principal detrás de esto es Mantenibilidad y legibilidad. Es fácil hacer que el código sea más legible y mantenible con la sentencia Switch / case luego if / else. Como tiene muchos if / else, el código se vuelve tan desordenado como el nido y es muy difícil mantenerlo.
Y de alguna forma, el tiempo de ejecución es otra razón.
Las instrucciones de cambio se pueden optimizar para la velocidad, pero pueden ocupar más memoria si los valores de caso se distribuyen en un gran número de valores.
if / else son generalmente lentos, ya que cada valor debe verificarse.
Recuerde también que las instrucciones de cambio permiten que el flujo de control continúe, lo que le permite combinar condiciones de manera agradable, al mismo tiempo que le permite agregar código adicional para ciertas condiciones, como en el siguiente fragmento de código:
switch (dayOfWeek)
{
case MONDAY:
garfieldUnhappy = true;
case TUESDAY:
case WEDNESDAY:
case THURSDAY:
case FRIDAY:
weekDay = true;
break;
case SATURDAY:
weekendJustStarted = true;
case SUNDAY:
weekendDay = true;
break;
}
En este if/else
usar sentencias if/else
no sería tan agradable en ningún lado.
if (dayOfWeek == MONDAY)
{
garfieldUnhappy = true;
}
if (dayOfWeek == SATURDAY)
{
weekendJustStarted = true;
}
if (dayOfWeek == MONDAY || dayOfWeek == TUESDAY || dayOfWeek == WEDNESDAY
|| dayOfWeek == THURSDAY || dayOfWeek == FRIDAY)
{
weekDay = true;
}
else if (dayOfWeek == SATURDAY || dayOfWeek == SUNDAY)
{
weekendDay = true;
}
Si hay muchos casos, la instrucción switch parece más limpia.
También es bueno cuando tienes múltiples valores para los que deseas el mismo comportamiento; simplemente usar múltiples sentencias "case" que se corresponden con una única implementación es mucho más fácil de leer que un if (esto || eso || someotherthing || .. .)
Switch / case generalmente se optimiza más eficientemente que if / else if / else, pero de vez en cuando (dependiendo del idioma y del compilador) se traduce a sentencias if / else if / else simples.
Personalmente, creo que las declaraciones de cambio hacen que el código sea más legible que un montón de sentencias if; siempre que sigas unas simples reglas. Reglas que probablemente deberías seguir incluso para tus situaciones if / else if / else, pero esa es otra vez mi opinión.
Esas reglas:
- Nunca, nunca, tenga más de una línea en su bloque de interruptores. Llame a un método o función y haga su trabajo allí.
- Siempre verifique la rotura / caso fallthrough.
- Burbujea excepciones.
También puede depender de su idioma: por ejemplo, algunos idiomas cambian solo funciona con tipos numéricos, por lo que le ahorra algo de tipeo cuando trabaja con un valor enumerado, constantes numéricas ... etc ...
If (day == DAYOFWEEK_MONDAY) {
//...
}
else if (day == DAYOFWEEK_TUESDAY) {
//...
}
//etc...
O un poco más fácil de leer ...
switch (day) {
case DAYOFWEEK_MONDAY :
//...
case DAYOFWEEK_TUESDAY :
//...
//etc...
}
Un Smalltalker podría rechazar ambos switch y if-then-else''s y podría escribir algo como: -
shortToLongDaysMap := Dictionary new.
shortToLongDaysMap
at: ''Mon'' put: ''Monday'';
at: ''Tue'' put: ''Tuesday'';
at: ''Wed'' put: ''Wednesday''
etc etc.
longForm := shortToLongDaysMap at: shortForm ifAbsent: [shortForm]
Este es un ejemplo trivial, pero espero que puedan ver cómo se escala esta técnica para un gran número de casos.
Tenga en cuenta el segundo argumento en at:IfAbsent:
es similar a la cláusula predeterminada de una declaración de caso.
abordando la preocupación de que todo dentro del interruptor tenga un alcance equivalente, siempre puedes arrojar la lógica de tu caso a otro {} bloque, como ese ...
switch( thing ) {
case ONETHING: {
int x; // local to the case!
...
}
break;
case ANOTHERTHING: {
int x; // a different x than the other one
}
break;
}
.. ahora no estoy diciendo que sea lindo. Simplemente ponerlo ahí como algo que es posible si es absolutamente necesario aislar algo en un caso de otro.
Otro pensamiento sobre el tema del alcance: parece una buena práctica poner solo un interruptor dentro de una función, y no mucho más. En esas circunstancias, el alcance de la variable no es tan preocupante, ya que de esa manera generalmente solo se trata de un caso de ejecución en cualquier invocación dada de la función.
bien, una última idea sobre los interruptores: si una función contiene más de un par de interruptores, es probable que sea hora de refactorizar su código. Si una función contiene conmutadores anidados , probablemente sea una pista para replantear un poco su diseño =)
recuerde que el caso / seleccionar proporciona flexibilidad adicional:
- la condición se evalúa una vez
- es lo suficientemente flexible como para construir cosas como el dispositivo de Duff
- Fallthrough (también conocido como caso sin interrupción)
así como se ejecuta mucho más rápido (a través de la tabla de salto / búsqueda) * históricamente
Resumiendo mi publicación inicial y comentarios: hay varias ventajas de la instrucción switch
sobre la declaración if
/ else
:
Código más limpio Código con múltiples cadenas
if
/else if ...
parece desordenado y es difícil de mantener: elswitch
proporciona una estructura más limpia.Actuación. Para los valores de
case
densos, el compilador genera la tabla de salto, para la búsqueda binaria dispersa o series deif
/else
, por lo que en el peor de los casos elswitch
es tan rápido comoif
/else
, pero típicamente más rápido. Aunque algunos compiladores también pueden optimizarif
/else
.El orden de prueba no importa. Para acelerar la serie de pruebas
if
/else
, primero hay que colocar los casos más probables. Con el programador deswitch
/case
no necesita pensar en esto.El valor predeterminado puede estar en cualquier lugar. Con
if
/else
el caso predeterminado debe estar al final, después del último. En elswitch
, eldefault
puede estar en cualquier lugar, siempre que el programador lo encuentre más apropiado.Código común. Si necesita ejecutar un código común para varios casos, puede omitir el
break
y la ejecución "desaparecerá", algo que no puede lograr conif
/else
. (Hay una buena práctica para colocar un comentario especial/* FALLTHROUGH */
para tales casos - lint lo reconoce y no se queja, sin este comentario se queja, ya que es un error común olvidar elbreak
).
Gracias a todos los comentaristas.