Devo usar instruções switch ou longas se ... else encadear?


36

Frequentemente, quando ouço sobre a declaração do switch, é adiada como uma maneira de substituir cadeias longas se ... else. Mas parece que quando eu uso a instrução switch, estou escrevendo mais código do que eu estaria escrevendo se ... mais. Você também tem outros problemas, como manter todas as variáveis ​​para todas as chamadas no mesmo escopo .

Aqui está um código que representa o fluxo que eu normalmente escrevo ( graças ao 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.";
}

Então eles querem que eu substitua isso por isso:

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 muito mais código em uma sintaxe muito mais estranha. Mas existe realmente uma vantagem em usar a instrução switch?


Ugh. Sim, isso certamente é mais volumoso, mas apenas na família C, porque sua sintaxe para instruções de caso é muito feia.
Mason Wheeler


1
Você deve evitar os dois sempre que possível. É muito melhor criar uma estrutura de dados e fazer uma pesquisa, mesmo que o destino da pesquisa seja uma função ou classe.
kevin cline

O switch é mais rápido, pelo menos em .net. Eu não sei sobre Java.
Knerd

Pode ser reformulado para um dicionário de casos e de métodos e um "se"
Pavel Yermalovich

Respostas:


57

Para esta situação em particular, parece-me que são ife casesão más escolhas. Eu usaria uma matriz simples:

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 uma observação lateral, você geralmente calcula o multiplicador com base no tamanho da matriz, em vez de codificar 3.

Como para quando se usar um caso / interruptor, a diferença a partir de uma cascata de ifdeclarações (ou, pelo menos, uma grande diferença) é que switchpode semi-automaticamente optimizar com base no número e densidade de valores, ao passo que uma cascata de ifdeclarações folhas do compilador com pouca opção a não ser gerar o código conforme você o escreveu, testando um valor após o outro até encontrar uma correspondência. Com apenas três casos reais, isso dificilmente é uma preocupação, mas com um número suficiente pode / pode ser significativo.


Esse exemplo foi apenas um exemplo BTW. Não sabia ainda que o compilador pode otimizar assim
TheLQ

6
@JBRWilkinson. Nesse caso, o valor fora dos limites só é possível através de um bug do compilador, no qual não estou disposto a gastar muito tempo (um bug no meu código para testar o resultado é quase tão provável quanto no código de geração). Em uma situação em que um valor fora dos limites era uma preocupação real (por exemplo, o índice estava sendo recebido de outro código), basta verificar os limites primeiro e usá-lo como índice somente após a verificação.
perfil completo de Jerry Coffin

4
Eu acho que esta resposta é muito específico sobre o exemplo, enquanto a pergunta era mais genérico do que isso ...
Khelben

3
@ Khelben: parece-me que você não se incomodou em ler toda a resposta. O último parágrafo aborda as questões mais amplas. Há um problema, porém: eu acho muito poucas situações em que considero quer uma casedeclaração ou uma cascata de ifdeclarações adequadas. Na maioria das vezes, eles são um substituto (medíocre) para algum tipo de coisa de mapa / matriz, e é melhor você usar um desses diretamente.
Jerry Coffin

1
@Titou: a menos que o compilador esteja totalmente convencido, o array será construído uma vez no tempo de compilação e, depois disso, você estará usando apenas uma estrutura estática. Por exemplo, se você estivesse fazendo isso em C ou C ++, desejaria transformar isso em uma static constmatriz, para garantir que ela sempre existisse (mas nenhuma linguagem foi dada na pergunta, então tentei não assumir uma na resposta também). )
Jerry Coffin

23

O problema com a if...else if...cadeia é que, quando eu leio para ler, tenho que analisar todas as ifcondições para entender o que o programa está fazendo. Por exemplo, você pode ter algo parecido com isto:

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 um pequeno número de declarações como essa, não é tão ruim)

Eu não teria como saber que você alterou a variável de condição no meio do caminho sem ler cada instrução. No entanto, como a switchlimita a apenas uma variável de condição, posso ver rapidamente o que está acontecendo.

No final do dia, porém, eu preferiria nem switchuma cadeia de if...else if. Freqüentemente, uma solução melhor é algum tipo de tabela de salto ou dicionário para casos como na pergunta original ou polimorfismo (se o seu idioma suportar isso). Nem sempre é possível, é claro, mas eu procuraria uma solução que evite o switchprimeiro passo ...


4
O polimorfismo tem a desvantagem de fragmentar o código, dificultando a compreensão em comparação a estar em um único local. Portanto, você pode hesitar um pouco antes de mudar para isso.

Por que uma tabela / dicionário de salto é melhor que um switch?
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;
}

A maneira acima de escrever esse tipo de caixa de mudança é bastante comum. A razão pela qual você sentiu a caixa de mudança se mais volumosa é porque seu corpo era apenas uma linha e, com uma caixa de troca, você também precisava da declaração de interrupção. Portanto, o gabinete do switch tinha o dobro do tamanho do corpo. Com um código mais substancial, a declaração de quebra não adicionará muito ao corpo. Para o corpo de linha única, é uma prática comum escrever o código na mesma linha que a instrução case.

Como outros já mencionaram, um caso de opção torna a intenção mais clara. Você deseja tomar uma decisão com base no valor de uma única variável / expressão. Meus comentários são puramente do ponto de vista da legibilidade e não são baseados em desempenho.


1
Se você colocar a opção em um método e tiver returna seqüência correta em cada caso , poderá eliminar as breakinstruções.
Robert Harvey

Sua solução também é a mais rápida.
Titou

8

Nesse caso, a instrução switch corresponde mais claramente à intenção do código: escolha uma ação a ser tomada com base em um único valor.

As declarações if, por outro lado, são muito mais difíceis de ler - você precisa examinar todas elas para ter certeza do que está acontecendo. Para mim, é menos código (mesmo se a contagem de caracteres pode ser um pouco maior), como há menos mentalmente analisar.


8

Eu concordo com Jerry que uma matriz de strings é melhor para esse caso em particular, mas que, em geral, é melhor usar uma instrução switch / case do que uma cadeia de elseifs. É mais fácil de ler e, às vezes, o compilador pode otimizar dessa maneira, mas também há outro benefício: é muito mais fácil depurar.

Quando você pressiona esse botão, você só precisa dar um passo para terminar no ramo direito, em vez de passar cuidadosamente por várias instruções if, uma de cada vez, e possivelmente pressionar a tecla muito rapidamente e passar por ela, perdendo alguma coisa e tendo Recomeçar.


3

Eu prefiro alternar nesses tipos de casos, ele corresponde muito melhor ao ponto do código, executa uma instrução diferente para cada valor de entrada diferente. Ele if..elseage mais como um "truque" para obter o mesmo efeito.

switch declarações também são mais limpas, é fácil ocultar erros de digitação em todos ==

Além disso, para grandes blocos em C, o switch é mais rápido.

else..ifpode ser mais apropriado quando você tem algo como intervalos (entre 1 e 100, faça isso, entre 100 e 200), ou em C, quando você tenta alternar com elementos como strings (isso é possível em outros idiomas). Qual é o mesmo.

Costumo usar muitas opções quando programa em C.


2

Escolha algo que seja eficiente, conciso e depois documente não apenas o que você fez, mas por quê.

O código pode ser revisitado, e nem sempre por seu autor original.

Há momentos em que você pode escolher deliberadamente uma implementação em detrimento de outra, porque pensa em um código que não existe.


2

Eu geralmente não gosto de nenhuma das abordagens. Long switch ou if apenas imploram para serem refatorados para uma abstração orientada a objeto (no entanto, seu exemplo eu classificaria como curto, não longo).

Pessoalmente, envolvia esse tipo de código em um 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);
}

A colocação do switch em um método separado permite colocar instruções de retorno diretamente dentro da instrução switch (pelo menos em c #), eliminando a necessidade de instruções de interrupção, tornando o código muito mais fácil de ler.

E isso é muito melhor do que a abordagem if / else if / else if.


3
Eu, pessoalmente, odeio a maneira "colocá-lo em outro método, porque parece feio" para resolver as coisas. Clutters lista de métodos e parece pior IMHO. Eu só faria isso se A) o código é duplicado em algum lugar ou B) poderia estar em outro lugar útil
TheLQ

1
que lista de métodos? e por que sua lista de métodos está sendo mais desordenada do que seu código? Pensei que tinha ultrapassado o "tudo o sustento em um único método para que você possa ver tudo de uma vez" idade
sara

@TheLQ Eu concordo com você em geral, mas neste mesmo caso o "comment =" é de fato consignado na proposta de Pete.
Titou

0

Em python, não há nenhuma instrução switch, porque se / elif / else é bom:

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"

Simples né?


Elifé apenas uma declaração if com algumas letras faltando. Definitivamente, é mais uma ifdeclaração do que uma declaração de troca. O fato de o Python NÃO ter uma opção faz com que quem os odeie (como eu) pense que não está sozinho.
Dan Rosenstark

A formatação Python funciona no stackoverflow, mas não no programmers.stackexchange.com :(
Christopher Mahan

Você deve alertá-los, a metamenos que seja um tópico conhecido. Obrigado por me dar uma testemunha.
Dan Rosenstark

ya, descobri que eles estão acontecendo em meta.programmers.stackexchange.com/questions/308/…
Christopher Mahan

1
@Yar, me lembra meus dias de administrador da wikipedia ... Oh, Joy. (estou completamente off-topic ainda?)
Christopher Mahan

0

Uma das coisas que torna o estilo C / C # switchparticularmente irritante é a insistência em que o casevalor seja literal. Uma coisa legal do VB / VB.NET é que select/casepermite que cada caso seja qualquer expressão booleana. Isso é conveniente. Na medida em que uma série de expressões booleanas mutuamente exclusivas geralmente é útil, uma série de if / else ifs é mais flexível, além de ser mais eficiente para digitar e ler.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.