Quem decidiu (e com base em quais conceitos) que a switch
construção (em várias línguas) deve usar break
em cada declaração?
Por que temos que escrever algo como isto:
switch(a)
{
case 1:
result = 'one';
break;
case 2:
result = 'two';
break;
default:
result = 'not determined';
break;
}
(notei isso em PHP e JS; provavelmente existem muitas outras linguagens usando isso)
Se switch
é uma alternativa de if
, por que não podemos usar a mesma construção que para if
? Ou seja:
switch(a)
{
case 1:
{
result = 'one';
}
case 2:
{
result = 'two';
}
default:
{
result = 'not determined';
}
}
Diz-se que break
impede a execução do bloco após o atual. Mas, alguém realmente se depara com a situação, onde havia necessidade de execução do bloco atual e dos seguintes? Eu não fiz. Para mim, break
está sempre lá. Em cada bloco. Em todo código.
conditions
trejder
fonte
fonte
CASE
instrução equivalente a um bloco if / elseif gigante.case 'a': case 'A': case 'b': case 'B'
mas principalmente porque não posso fazercase in [ 'a', 'A', 'b', 'B' ]
. Uma pergunta um pouco melhor é que, no meu idioma preferido atual (C #), a interrupção é obrigatória e não há queda implícita; o esquecimentobreak
é um erro de sintaxe ...: \case TOKEN_A: /*set flag*/; case TOKEN_B: /*consume token*/; break; case TOKEN_C: /*...*/
break
estiver presente em qualquer lugar” é uma regra significativamente mais simples de implementar do que “Não emitir um salto sefallthrough
estiver presente em umswitch
”.Respostas:
C foi um dos primeiros idiomas a ter a
switch
declaração nesse formato, e todos os outros idiomas principais a herdaram a partir daí, optando principalmente por manter a semântica C por padrão - eles não pensaram nas vantagens de alterá-la ou julgaram menos importante do que manter o comportamento com o qual todos estavam acostumados.Quanto ao motivo pelo qual o C foi projetado dessa maneira, provavelmente deriva do conceito de C como "montagem portátil". A
switch
instrução é basicamente uma abstração de uma tabela de ramificação , e uma tabela de ramificação também possui uma queda implícita e requer uma instrução de salto adicional para evitá-la.Então, basicamente, os designers de C também escolheram manter a semântica do assembler por padrão.
fonte
switchon
.when
.Porque
switch
não é uma alternativa deif ... else
declarações nesses idiomas.Ao usar
switch
, podemos combinar mais de uma condição por vez, o que é muito apreciado em alguns casos.Exemplo:
fonte
more than one block ... unwanted in most cases
Eu não estou de acordo com você.break
. Mas sim, tanto quanto sei, o dispositivo de Duff não funciona em c #.C# does not support an implicit fall through from one case label to another
. Você pode fracassar, mas não há chance de esquecer acidentalmente uma pausa.if..else
mas em algumas situaçõesswitch..case
seria preferível. O exemplo acima seria um pouco complexo se usarmos if-else.Isso foi perguntado no Stack Overflow no contexto de C: Por que a instrução switch foi projetada para precisar de uma interrupção?
Para resumir a resposta aceita, provavelmente foi um erro. A maioria das outras linguagens provavelmente apenas seguiu o C. No entanto, algumas linguagens como o C # parecem ter corrigido isso ao permitir falhas - mas somente quando o programador o explicita (fonte: link acima, eu não falo C #) .
fonte
/* FALLTHRU */
comentário na resposta vinculada pelo @Tapio acima, para provar que você quis dizer isso.goto case
, como o link faz, não ilustra por que isso funciona tão bem. Você ainda pode empilhar vários casos, como acima, mas assim que inserir o código, você precisará ter um explícitogoto case whatever
para passar para o próximo caso ou obter um erro no compilador. Se você me perguntar, uma das duas, a capacidade de empilhar casos sem se preocupar com falhas inadvertidas por negligência é de longe um recurso melhor do que permitir uma falha explícitagoto case
.Eu responderei com um exemplo. Se você deseja listar o número de dias para cada mês do ano, é óbvio que alguns meses têm 31, 30 e 1 28/29. Ficaria assim,
Este é um exemplo em que vários casos têm o mesmo efeito e são todos agrupados. Obviamente, havia uma razão para a escolha da palavra-chave break e não a construção if ... else if .
O principal a ser observado aqui é que uma declaração switch com muitos casos semelhantes não é um if ... else se ... else if ... else para cada um dos casos, mas sim se (1, 2, 3) ... senão se (4,5,6) senão ...
fonte
if
sem nenhum benefício. Talvez se seu exemplo atribuísse um nome ou fizesse um trabalho específico para o mês além do fall-through, a utilidade do fall-through seria mais aparente? (sem comentar sobre se se deve, em primeiro lugar)Há duas situações em que pode ocorrer a ocorrência de um caso para outro - o caso vazio:
e o caso não vazio
Não obstante a referência ao dispositivo de Duff , as instâncias legítimas para o segundo caso são poucas e distantes entre si, e geralmente proibidas pelos padrões de codificação e sinalizadas durante a análise estática. E onde é encontrado, é mais frequentemente do que não devido à omissão de a
break
.O primeiro é perfeitamente sensato e comum.
Para ser sincero, não vejo razão para precisar do
break
analisador e do idioma, sabendo que um corpo de caso vazio é uma falha, enquanto um caso não vazio é autônomo.É uma pena que o painel ISO C pareça mais preocupado em adicionar recursos novos (indesejados) e mal definidos ao idioma, em vez de corrigir os recursos indefinidos, não especificados ou definidos pela implementação, sem mencionar os ilógicos.
fonte
Não forçar o
break
permite uma série de coisas que poderiam ser difíceis de fazer. Outros observaram casos de agrupamento, para os quais existem vários casos não triviais.Um caso em que é imperativo que o
break
não seja usado é o dispositivo de Duff . Isso é usado para " desenrolar " loops, onde pode acelerar as operações, limitando o número de comparações necessárias. Acredito que o uso inicial permitia funcionalidades que antes eram muito lentas com um loop totalmente enrolado. Ele troca o tamanho do código pela velocidade em alguns casos.É uma boa prática substituir o
break
comentário por um apropriado, se o caso tiver algum código. Caso contrário, alguém irá corrigir os desaparecidosbreak
e introduzir um erro.fonte
Em C, onde a origem parece estar, o bloco de código da
switch
instrução não é uma construção especial. É um bloco de código normal, assim como um bloco em umaif
instrução.case
edefault
são etiquetas de salto dentro deste bloco, especificamente relacionadas aswitch
. Eles são tratados da mesma maneira que os rótulos de salto normaisgoto
. Há uma regra específica importante aqui: os rótulos de salto podem estar em quase toda parte do código, sem interromper o fluxo do código.Como um bloco de código normal, ele não precisa ser uma instrução composta. Os rótulos também são opcionais. Estas são
switch
instruções válidas em C:O próprio padrão C fornece isso como um exemplo (6.8.4.2):
Além disso, também
default
é um rótulo de salto e, portanto, pode estar em qualquer lugar, sem a necessidade de ser o último caso.Isso também explica o dispositivo de Duff:
Por que a queda? Como no fluxo de código normal em um bloco de código normal, é esperado o fall-through para a próxima instrução , assim como você esperaria em um
if
bloco de código.Meu palpite é que o motivo disso foi a facilidade de implementação. Você não precisa de código especial para analisar e compilar um
switch
bloco, cuidando de regras especiais. Você apenas o analisa como qualquer outro código e precisa cuidar apenas dos rótulos e da seleção de salto.Uma questão interessante de acompanhamento disso tudo é se as seguintes declarações aninhadas imprimirem "Concluído". ou não.
O padrão C cuida disso (6.8.4.2.4):
fonte
Várias pessoas já mencionaram a noção de combinar várias condições, o que é muito valioso de tempos em tempos. No entanto, a capacidade de corresponder a várias condições não requer necessariamente que você faça exatamente a mesma coisa com cada condição que corresponda. Considere o seguinte:
Existem duas maneiras diferentes pelas quais conjuntos de várias condições são correspondidos aqui. Nas condições 1 e 2, eles simplesmente caem no mesmo lote de código e fazem exatamente a mesma coisa. Com as condições 3 e 4, no entanto, embora ambas terminem chamando
doSomething3And4()
, apenas 3 chamamdoSomething3()
.fonte
case 2
acionamento, portanto, se queremos estar corretos (e fazemos) o real o nome da função teria que serdoSomethingBecause_1_OR_2_OR_1AND2()
ou algo assim (embora o código legítimo no qual um imutável corresponda a dois casos diferentes enquanto ainda esteja sendo bem escrito seja praticamente inexistente, portanto, pelo menos isso deve serdoSomethingBecause1or2()
)doSomething[ThatShouldBeDoneInTheCaseOf]1And[InTheCaseOf]2()
,. Não se refere a lógica e / ou.Para responder a duas de suas perguntas.
Por que C precisa de pausas?
Tudo se resume às raízes do Cs como um "montador portátil". Onde o código psudo como este era comum: -
a instrução switch foi projetada para fornecer funcionalidade semelhante em um nível superior.
Nós sempre temos interruptores sem interrupções?
Sim, isso é bastante comum e existem alguns casos de uso;
Primeiramente, você pode querer executar a mesma ação em vários casos. Fazemos isso empilhando os casos uns sobre os outros:
Outro caso de uso comum em máquinas de estado é que, após o processamento de um estado, queremos entrar e processar imediatamente outro estado:
fonte
A declaração de interrupção é uma declaração de salto que permite ao usuário sair do comutador anexo mais próximo (para o seu caso), enquanto, do, para ou foreach. é tão fácil quanto isso.
fonte
Eu acho que, com tudo o que foi escrito acima - o principal resultado desse segmento é que, se você deseja criar um novo idioma - o padrão deve ser que você não precise adicionar uma
break
declaração e o compilador a tratará como se você o fizesse.Se você deseja aquele caso raro em que deseja continuar para o próximo caso - basta declarar com uma
continue
declaração.Isso pode ser aprimorado para que somente se você usar colchetes dentro do gabinete, ele não continuará, para que o exemplo com os meses acima de vários casos executando o mesmo código exato - funcione sempre como esperado sem a necessidade de
continue
.fonte
*
,&
,static
etc.