¿Quién decidió (y con base en qué conceptos) que la switch
construcción (en muchos idiomas) tiene que usar break
en cada enunciado?
¿Por qué tenemos que escribir algo como esto?
switch(a)
{
case 1:
result = 'one';
break;
case 2:
result = 'two';
break;
default:
result = 'not determined';
break;
}
(Noté esto en PHP y JS; probablemente hay muchos otros lenguajes que usan esto)
Si switch
es una alternativa de if
, ¿por qué no podemos usar la misma construcción que para if
? Es decir:
switch(a)
{
case 1:
{
result = 'one';
}
case 2:
{
result = 'two';
}
default:
{
result = 'not determined';
}
}
Se dice que break
impide la ejecución del bloque que sigue al actual. Pero, ¿alguien realmente se encuentra con la situación, donde había alguna necesidad de ejecutar el bloque actual y los siguientes? No lo hice Para mí, break
siempre está ahí. En cada cuadra. En cada codigo.
conditions
trejder
fuente
fuente
CASE
declaración de manera equivalente a un bloque gigante if / elseif.case 'a': case 'A': case 'b': case 'B'
pero principalmente porque no puedo hacerlocase in [ 'a', 'A', 'b', 'B' ]
. Una pregunta un poco mejor es, en mi lenguaje preferido actual (C #), el descanso es obligatorio , y no hay una caída implícita; olvidarbreak
es un error de sintaxis ...: \case TOKEN_A: /*set flag*/; case TOKEN_B: /*consume token*/; break; case TOKEN_C: /*...*/
break
está presente en alguna parte" es una regla significativamente más simple de implementar que "No emitir un salto sifallthrough
está presente en unswitch
".Respuestas:
C fue uno de los primeros lenguajes en tener la
switch
declaración en esta forma, y todos los demás lenguajes principales la heredaron de allí, en su mayoría eligieron mantener la semántica de C por defecto; o no pensaron en las ventajas de cambiarla o juzgaron menos importantes que mantener el comportamiento al que todos estaban acostumbrados.En cuanto a por qué C fue diseñado de esa manera, probablemente se deriva del concepto de C como "ensamblaje portátil". La
switch
declaración es básicamente una abstracción de una tabla de ramificación , y una tabla de ramificación también tiene una caída implícita y requiere una instrucción de salto adicional para evitarla.Básicamente, los diseñadores de C también optaron por mantener la semántica del ensamblador por defecto.
fuente
switchon
.when
.Porque
switch
no es una alternativa deif ... else
declaraciones en esos idiomas.Al usarlo
switch
, podemos igualar más de una condición a la vez, lo cual es muy apreciado en algunos casos.Ejemplo:
fuente
more than one block ... unwanted in most cases
No estoy de acuerdo contigo.break
. Pero sí, que yo sepa, el dispositivo de Duff no funciona en C #.C# does not support an implicit fall through from one case label to another
. Puede fallar, pero no hay posibilidad de olvidar accidentalmente un descanso.if..else
pero en algunas situacionesswitch..case
sería preferible. El ejemplo anterior sería un poco complejo si utilizamos if-else.Esto se ha preguntado en Stack Overflow en el contexto de C: ¿Por qué se diseñó la instrucción switch para necesitar un descanso?
Para resumir la respuesta aceptada, probablemente fue un error. La mayoría de los otros lenguajes probablemente solo han seguido a C. Sin embargo, algunos lenguajes como C # parecen haber solucionado esto al permitir la caída, pero solo cuando el programador lo dice explícitamente (fuente: el enlace de arriba, no hablo C #) .
fuente
/* FALLTHRU */
comentario en la respuesta vinculada por @Tapio arriba, para demostrar que lo dices en serio .goto case
, como lo hace el enlace, no ilustra por qué eso funciona tan bien. Todavía puede apilar varios casos como el anterior, pero tan pronto como inserte el código, debe tener un mensaje explícitogoto case whatever
para pasar al siguiente caso u obtener un error del compilador. Si me pregunta, de los dos, la capacidad de apilar casos sin preocuparse por una caída accidental por negligencia es, con mucho, una mejor característica que la habilitación de la caída explícitagoto case
.Contestaré con un ejemplo. Si desea enumerar la cantidad de días para cada mes del año, es obvio que algunos meses tienen 31, algunos 30 y 1 28/29. Se vería así
Este es un ejemplo en el que varios casos tienen el mismo efecto y están todos agrupados. Obviamente, había una razón para elegir la palabra clave break y no la construcción if ... else if .
Lo principal a tener en cuenta aquí es que una declaración de cambio con muchos casos similares no es un if ... else if ... else if ... else para cada uno de los casos, sino más bien if (1, 2, 3) ... si if (4,5,6) más ...
fuente
if
sin ningún beneficio. ¿Quizás si su ejemplo asignara un nombre o hiciera un trabajo específico para un mes además de la falla, la utilidad de la falla sería más evidente? (sin comentar si uno DEBE en primer lugar)Hay dos situaciones en las que puede ocurrir un "fracaso" de un caso a otro: el caso vacío:
y el caso no vacío
A pesar de la referencia al dispositivo de Duff , las instancias legítimas para el segundo caso son pocas y distantes, y generalmente están prohibidas por los estándares de codificación y marcadas durante el análisis estático. Y donde se encuentra, la mayoría de las veces se debe a la omisión de a
break
.El primero es perfectamente sensible y común.
Para ser honesto, no puedo ver ninguna razón para necesitar el
break
analizador de lenguaje y saber que un cuerpo de caso vacío es una falla, mientras que un caso no vacío es independiente.Es una pena que el panel ISO C parezca más preocupado por agregar características nuevas (no deseadas) y mal definidas al lenguaje, en lugar de corregir las características indefinidas, no especificadas o definidas por la implementación, sin mencionar lo ilógico.
fuente
No forzar el
break
permite una serie de cosas que de otro modo podrían ser difíciles de hacer. Otros han señalado casos de agrupación, para los cuales hay una serie de casos no triviales.Un caso en el que es imperativo que
break
no se use es el dispositivo de Duff . Esto se utiliza para " desenrollar " bucles donde puede acelerar las operaciones limitando el número de comparaciones requeridas. Creo que el uso inicial permitió una funcionalidad que anteriormente había sido demasiado lenta con un ciclo completamente enrollado. Se cambia el tamaño del código por velocidad en algunos casos.Es una buena práctica reemplazar el
break
con un comentario apropiado, si el caso tiene algún código. De lo contrario, alguien reparará lo que faltabreak
e introducirá un error.fuente
En C, donde parece estar el origen, el bloque de código de la
switch
declaración no es una construcción especial. Es un bloque de código normal, al igual que un bloque debajo de unaif
instrucción.case
ydefault
son etiquetas de salto dentro de este bloque, específicamente relacionadas conswitch
. Se manejan como las etiquetas de salto normalesgoto
. Aquí hay una regla específica que es importante: las etiquetas de salto pueden estar en casi todas partes del código, sin interrumpir el flujo del código.Como un bloque de código normal, no necesita ser una declaración compuesta. Las etiquetas también son opcionales. Estas son
switch
declaraciones válidas en C:El estándar C en sí mismo da esto como un ejemplo (6.8.4.2):
Además, también
default
es una etiqueta de salto, y por lo tanto puede estar en cualquier lugar, sin la necesidad de ser el último caso.Esto también explica el dispositivo de Duff:
¿Por qué la caída? Debido a que en el flujo de código normal en un bloque de código normal, se espera pasar a la siguiente instrucción , tal como lo esperaría en un
if
bloque de código.Supongo que la razón de esto fue la facilidad de implementación. No necesita un código especial para analizar y compilar un
switch
bloque, teniendo en cuenta las reglas especiales. Simplemente lo analiza como cualquier otro código y solo tiene que cuidar las etiquetas y la selección de salto.Una pregunta de seguimiento interesante de todo esto es si las siguientes declaraciones anidadas imprimen "Listo". o no.
El estándar C se preocupa por esto (6.8.4.2.4):
fuente
Varias personas ya han mencionado la noción de emparejar múltiples condiciones, lo cual es muy valioso de vez en cuando. Sin embargo, la capacidad de hacer coincidir múltiples condiciones no necesariamente requiere que haga exactamente lo mismo con cada condición que coincida. Considera lo siguiente:
Aquí hay dos formas diferentes de hacer coincidir conjuntos de condiciones múltiples. Con las condiciones 1 y 2, simplemente caen exactamente en el mismo diagrama de código y hacen exactamente lo mismo. Sin embargo, con las condiciones 3 y 4, aunque ambas terminan llamando
doSomething3And4()
, solo 3 llamadasdoSomething3()
.fuente
case 2
activación, por lo que si queremos ser correctos (y lo hacemos) lo real el nombre de la función tendría que serdoSomethingBecause_1_OR_2_OR_1AND2()
o algo (aunque el código legítimo en el que un inmutable coincide con dos casos diferentes sin dejar de ser un código bien escrito es prácticamente inexistente, por lo menos esto debería serdoSomethingBecause1or2()
)doSomething[ThatShouldBeDoneInTheCaseOf]1And[InTheCaseOf]2()
. No se refiere a lógica y / o.Para responder dos de tus preguntas.
¿Por qué C necesita descansos?
Se reduce a las raíces Cs como un "ensamblador portátil". Donde el código psudo como este era común:
La declaración de cambio se diseñó para proporcionar una funcionalidad similar en un nivel superior.
¿Alguna vez tenemos interruptores sin interrupciones?
Sí, esto es bastante común y hay algunos casos de uso;
En primer lugar, es posible que desee realizar la misma acción para varios casos. Hacemos esto apilando los casos uno encima del otro:
Otro caso de uso común en las máquinas de estado es que después de procesar un estado, inmediatamente queremos ingresar y procesar otro estado:
fuente
La declaración de interrupción es una declaración de salto que permite al usuario salir del interruptor de cierre más cercano (para su caso), mientras, hacer, para o para cada uno. Es tan fácil como eso.
fuente
Creo que con todo lo escrito anteriormente, el resultado principal de este hilo es que si va a diseñar un nuevo lenguaje, lo predeterminado debería ser que no necesita agregar una
break
declaración y el compilador lo tratará como si lo hiciera.Si desea ese caso raro en el que desea continuar con el siguiente caso, simplemente indíquelo con una
continue
declaración.Esto se puede mejorar para que solo si usa llaves dentro de la caja, no continúe, de modo que el ejemplo con los meses anteriores de varias cajas que realizan el mismo código exacto, siempre funcionaría como se esperaba sin la necesidad de
continue
.fuente
*
,&
,static
etc.