Fim do fluxo If-Else VS Switch

8

Eu queria saber o que if-else declarações, é como uma instrução switch que faz ter uma instrução break.

if( boolean_expression_1 )
  statement_1
else if( boolean_expression_2 )
  statement_2
else 
  default_statement

É o mesmo que:

switch( controlling_expression )
{
case: ( boolean_expression_1 )
{
     statement_1
     break;
}
case: ( boolean_expression_2 )
{
     statement_2
     break;
}
default:
{
     default_statement
}
Chris Okyen
fonte
Alterei a pergunta para o que eu realmente quis dizer, QUAL O OPOSTO do que eu originalmente perguntei. Exemplo editado para corresponder à pergunta pretendida. Sei que alguns de vocês perguntaram se as duas declarações eram aparentemente iguais e outras responderam se eram literalmente iguais. Agradeço a ambos, uma vez que dá um ângulo de thougt de similiarities preseseved e também o que eles são (diferente / iguais) no fundo
Chris Okyen
Se você achar esses tipos de construções interessantes, eu recomendo dar uma olhada nos condicionais e guardas de correspondência de padrões. Os conceitos são muito semelhantes, mas podem ser maravilhosamente expressivos. Estou familiarizado com eles em F #, mas na JVM, acredito que o Scala os apóia.
Steven Evers

Respostas:

6

Eu queria saber as instruções if-else, é como uma instrução switch que não possui uma instrução break.

Isso é ao contrário. Mais ou menos.

Uma sequência if, else if, ... else, na qual todos os testes são simples testes de igualdade na mesma variável do formulário (variable == value)ou ((variable == value1) || (variable == value2) ...)podem ser convertidos em uma instrução switch nessa variável. Os (variable == value)testes se tornam case value:e o corpo do if(ou else if) se torna o corpo do caso, mas você precisa adicionar uma breakdeclaração no final do corpo do caso. Os ((variable == value1) || (variable == value2) ...)testes se tornam uma sequência de casos do formato case value1: case value2: ... case valuen:An if sequência com testes mais complexos é, em geral, distinta de uma instrução switch.

Se todos os casos em uma instrução switch terminarem com break, essa instrução switch poderá sempre ser reescrita como uma sequência if, else if, .... Essa conversão de uma mudança para uma sequência if também é possível quando a queda é limitada a coisas como case A: case B: ... case N: do_something(); break;Fall, na qual um corpo não vazio cai para outro corpo não vazio, não é diretamente conversível em uma sequência if.

David Hammen
fonte
Obrigado. As instruções if-else são percebidas / aparentemente alternam as instruções (...) com uma quebra em cada extremidade de cada corpo do caso. Múltiplas instruções if sozinhas sem um outro par , são instruções percebidas / aparentemente comutadas (...) com No breaks no final de cada corpo do caso. Eu descreveria como isso é percebido, adicionando os detalhes do aninhamento, mas isso estaria fora de escopo / mais para mastigar o pedaço dessa pergunta.
22612 Chris Okyen
3

Embora pensar em um bloco if-else como uma instrução switch sem interrupção possa ser uma abstração útil para um novo programador, não é uma comparação perfeita.

O switch provavelmente está mais próximo de "ir para". Concedido, Java não tem Goto, mas a linguagem foi construída com base em idéias de outras linguagens como C que possuíam. Os rótulos "case" e "default" não são realmente diferentes de qualquer outro destino de salto para uma instrução goto; portanto, o comportamento esperado seria continuar executando a próxima linha de código dentro do bloco (a instrução switch inteira é o bloco) . Isso é explicado mais adiante em uma das respostas de uma pergunta relacionada: Por que precisamos usar o interruptor de interrupção? Portanto, terminamos com o caso em que você precisa de uma declaração de interrupção para informar ao seu programa que você executou esse bloco. Você pode usar quebra em outros tipos de instruções, como um loop for ou while da mesma forma.

Em idiomas como java que usam curto-circuito, em um bloco if-elseif-else, se a condição do primeiro caso da instrução if for verdadeira, as condições do bloco else if e else não serão avaliadas. Depois de um se é esperado que você encontre um bloco de chaves entre códigos {} ou uma única instrução. Por definição, else significa "caso contrário" ou que a condição anterior não era verdadeira, seria um pouco contraditório deixar o código cair.

Jessica Brown
fonte
"Embora pensar em um bloco 'if-else' como sendo uma 'declaração de troca' sem interrupção possa ser uma abstração útil para um novo programador, não é uma comparação perfeita". - Você não quer dizer "pensar em um bloco 'if-else' como uma 'declaração de troca' com um intervalo ...?"
Chris Okyen
Contraditório é certo!
22612 Chris Okyen
1

As linguagens de programação geralmente fornecem muitas construções diferentes, que permitem expressar lógica semelhante usando sintaxe diferente.

ifAs elsedeclarações / não "caem" umas nas outras incondicionalmente; a palavra-chave else impede que outras else-ifcondições sejam avaliadas da mesma maneira que breakno final de um caso-bloco o tira de um comutador, no entanto, a criação de um conjunto de ifs independentes (sem usar else) fará com que cada ifcondição seja avaliada por conta própria.

De maneira abrangente, a remoção da breakpalavra-chave do final de seus caseblocos fará com que o fluxo de execução continue incondicionalmente até o próximo caso até que uma quebra / retorno / goto seja encontrada ou o fim do bloco de comutação seja atingido.

Existem (raramente) situações em que a descoberta de casos de troca é considerada útil; no entanto, é mais comum e geralmente mais idiomático querer que cada bloco de casos seja tratado por conta própria.

if-elsee switch-casesão duas construções que permitem expressar a seleção (tomada de decisão) em uma linguagem de programação; por vezes, a escolha entre os dois é baixo para o estilo pessoal, outras vezes há razões para escolher um sobre o outro - Uma escolha semelhante existe quando se escolhe para versus enquanto para expressar repetição.

Ben Cottrell
fonte
Obrigado. Um exemplo de situações em que a interrupção de caso de troca é considerada útil é quando, se você deseja que um caso faça alguma coisa e possivelmente permita que algo mais aconteça após um resultado real para esse caso, se o próximo caso também for verdadeiro ... switch (i) case: Um {break; } Caso: B {....} caso: C {.... pausa} Então, se B ocorre e, em seguida, permite a possibilidade de C ocorrendo ...
Chris Okyen
0

A resposta é não - a instrução if não é como uma instrução switch sem interrupção. Vamos começar com esta parte da sua pergunta:

se boolean_expression_1 fosse true, verificaria se boolean_expression_2 é true?

Podemos encontrar a resposta com este programa. O truque é que chamamos um método como nossa expressão booleana e usamos System.out.println () para ver se o método é executado. Dessa forma, sabemos se a expressão foi avaliada. Aqui está o programa:

public class IfElseExample {

    private static boolean booleanExpression1() {
        System.out.println("Expression 1 is being evaluated.");
        return true;
    }

    private static boolean booleanExpression2() {
        System.out.println("Expression 2 is being evaluated.");
        return true;
    }

    public static void main(String[] args) {
        if (booleanExpression1()) 
            System.out.println("Statement 1 is executed");
        else if (booleanExpression2()) 
            System.out.println("Statement 2 is executed");
        else
            System.out.println("Default statement is executed");
    }

}

Você verá que a segunda expressão booleana não é avaliada. Esse comportamento provavelmente se torna mais compreensível se você colocar alguns colchetes (o que é um bom hábito na maioria dos casos):

    if (booleanExpression1()) { 
        System.out.println("Statement 1 is executed");
    } else {
        if (booleanExpression2()) {
            System.out.println("Statement 2 is executed");
        } else {
            System.out.println("Default statement is executed");
        }
    }

É assim que o compilador visualiza sua cadeia de if original. Como você pode ver agora, o segundo "se" é uma declaração única e, como tal, não está no mesmo nível que a primeira declaração se. Se você encadear mais instruções if, elas ficam aninhadas ainda mais.

Você também perguntou por que a instrução switch precisa de uma instrução break. Isso ocorre porque a instrução switch não é implementada internamente com uma cadeia de instruções if (nem a instrução if com base nas instruções switch). Você pode esperar isso

        switch(controllingExpression()) {
            case 42: System.out.println("A");
                        case 43: System.out.println("B");
            default : System.out.println("z");
        }

é traduzido pelo compilador em algo como:

if (controllingExpression() == 42) {
    System.out.println("A");
} else if (controllingExpression() == 43) {
    System.out.println("B");
} else {
    System.out.println("z");
}

Este não é o caso. Em vez disso, ele usa uma instrução skip oculta:

int x = numberOfStatementsToSkipFor(controllingExpression());
skip x // hidden skip the next x statements byte code instruction
System.out.println("A");
System.out.println("B");
System.out.println("z");

O método numberOfStatementsToSkipFor () retorna 0 para 42, 1 para 43 e 2 para todo o resto. Agora fica claro por que esse programa pode produzir

A
B
z

ou

B
z

ou apenas

z

mas nunca

A
B

sem o 'z'. Mas claramente seria bom se pudéssemos fazer isso. E podemos usar o break, que é traduzido em outra instrução skip. Então, se você colocar quebra em todos os ramos do caso

switch(controllingExpression()) {
    case 42: {
        System.out.println("A");
        break;
    }
    case 43: {
        System.out.println("B");
        break;
    }
    default : System.out.println("z");
}

o compilador produzirá isso:

int x = numberOfStatementsToSkip(controllingExpression());
skip x; // hidden skip the next x statements byte code instruction
System.out.println("A");
skip 3; //"B" + skip + "z"
System.out.println("B");
skip 1;
System.out.println("z");
scarfridge
fonte
Obrigado, eu entendi tudo até o 'int x = numberOfStatementsToSkipFor (controllerExpression ());' pedaço de código que descreve como ele pulará o próximo número x de instruções de bytecode se você tiver uma instrução switch como mostrou. O compilador transforma a instrução switch no código que diz para ignorar o X # do bytecode e a JVM faz isso?
Chris Okyen
Também não entendo como esse processo de pular funciona ... Conheço algumas M6800 asm, é como pular o ponteiro para a quantidade de pilha X de chamadas ... Nem todas as instruções dos 2 casos e o padrão são chamadas assim por que eles seriam pulados. Como ele sabe ignorá-los. E por que você pulou apenas as instruções (ou ações) chamadas devido aos casos serem verdadeiros ... A troca {e case: code e default: e} code precisam ser ignoradas no código operacional ou isso não é real "opcode", mas apenas lógica colocada em linguagens de programação de nível superior como C e Java e Pascal?
Chris Okyen
@ChrisOkyen Eu estava tentando ilustrar o conceito básico. A realização real na JVM é descrita aqui: artima.com/underthehood/flowP.html
scarfridge
0

Para responder à pergunta editada (com os intervalos)

Eles avaliam as instruções da mesma forma, se e somente se as condições para inserir cada bloco forem as mesmas.

Para responder à questão subjetiva, de

dá um ângulo de pensamento das semelhanças preservadas e também o que elas são (diferentes / iguais) no fundo

A comparação dos dois o distrai do que cada um deles foi projetado para expressar rapidamente.

A switchcentraliza sintaticamente a comparação para que você possa acompanhar o fluxo com base em seus casos.

Enquanto um ifdescreve uma única condição para inserir um bloco.

Basicamente, o código "lê" de maneira diferente.

Por exemplo, você tem um wordna sua cabeça e deseja procurá-lo no dicionário.

Um interruptor seria como: segure a letra inicial em sua cabeça e, em seguida, folhear o alfabeto e desistir se não começar com essa letra.

Enquanto um monte de blocos if-else seria como: a palavra começa com a letra? repetido várias vezes e depois desista.

Se você está tentando explicar como um comutador funciona para um novo programador (que esteja familiarizado com os blocos if, pode ser mais fácil explicá-lo como:

if( boolean_expression_1 ) {
  statement_1;
  return;
}

if( boolean_expression_2 ) {
  statement_2;
  return;
}

default_statement;
return;

Agora, o exemplo anterior quebra o que normalmente é ensinado nas escolas, mas o retorno antecipado e o retorno frequentemente são às vezes mais fáceis de ler e tão rápido em uma pilha baseada em vm.

Deve haver otimizações para comutadores em primitivas, portanto, a escolha de qual bloco de controle usar deve ser baseada no que você está comparando.

Lembre-se de que declarações longas switche iftendem a se tornar muito detalhadas, difíceis de manter e também degradam o desempenho, pois mais condições precisariam ser verificadas.

Você pode considerar uma Hashclasse / função anônima ou um padrão de comando, em vez de longos blocos de controle com muitas condições.

Hashes e padrão de comando permitem que o código tenha um desempenho mais rápido e melhoram a capacidade de manutenção do código.

Brian
fonte