If-Else VS Switch fin de flujo

8

Me preguntaba si la si-else, es como una sentencia switch que lo hace tener una sentencia break.

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

Es lo mismo que:

switch( controlling_expression )
{
case: ( boolean_expression_1 )
{
     statement_1
     break;
}
case: ( boolean_expression_2 )
{
     statement_2
     break;
}
default:
{
     default_statement
}
Chris Okyen
fuente
Cambié la pregunta a lo que realmente quería decir, que era lo opuesto a lo que originalmente pregunté. Ejemplo editado para que coincida con la pregunta prevista. Sé que algunos de ustedes respondieron si las dos declaraciones eran aparentemente iguales, y otras respondieron si eran literalmente iguales. Aprecio los dos, ya que da un ángulo de miles de similitudes previamente preservadas y también lo que son (diferentes / iguales) en el fondo
Chris Okyen
Si encuentra este tipo de construcciones interesantes, le recomiendo que eche un vistazo a los condicionales y protectores de coincidencia de patrones. Los conceptos son muy similares pero pueden ser maravillosamente expresivos. Estoy familiarizado con ellos en F # pero en JVM, creo que Scala los admite.
Steven Evers

Respuestas:

6

Me preguntaba si las declaraciones if-else, son como una declaración switch que no tiene una declaración break.

Eso es al revés. Mas o menos.

Una secuencia if, else if, else en la que todas las pruebas son pruebas de igualdad simples en la misma variable del formulario (variable == value)o ((variable == value1) || (variable == value2) ...)se pueden convertir en una declaración de cambio en esa variable. Las (variable == value)pruebas se convierten case value:y el cuerpo de if(o else if) se convierte en el cuerpo del caso, pero debe agregar una breakdeclaración al final del cuerpo del caso. Las ((variable == value1) || (variable == value2) ...)pruebas se convierten en una secuencia de casos de la forma case value1: case value2: ... case valuen:Una secuencia if con pruebas más complejas es en general distinta de una declaración de cambio.

Si cada caso en una declaración de cambio termina con break, esa declaración de cambio siempre se puede reescribir como una secuencia if, sino if, ... Esta conversión de un cambio a una secuencia if también es posible cuando la caída se limita a cosas como case A: case B: ... case N: do_something(); break;Fall through en la que un cuerpo no vacío cae a otro cuerpo no vacío no se puede convertir directamente en una secuencia if.

David Hammen
fuente
Gracias. las declaraciones if-else se perciben / aparentemente cambian (...) las declaraciones con un corte en cada extremo de cada cuerpo del caso. Múltiples declaraciones if solas sin un par else , se perciben / aparentemente cambian (...) las declaraciones sin interrupciones al final de cada cuerpo de caso. Describiría cómo se percibe, agregando los detalles de anidación, pero eso estaría fuera del alcance / más para analizar la parte de esta pregunta.
Chris Okyen
3

Aunque pensar en un bloque if-else como una declaración de cambio sin interrupción podría ser una abstracción útil para un nuevo programador, no es una comparación perfecta.

El interruptor probablemente esté más relacionado con "goto". Por supuesto, Java no tiene Goto, pero el lenguaje se construyó sobre ideas de otros lenguajes como C que sí. Las etiquetas "case" y "default" no son realmente diferentes a cualquier otro objetivo de salto para una instrucción goto, por lo que el comportamiento esperado sería continuar ejecutando la siguiente línea de código dentro del bloque (toda la instrucción switch es el bloque) . Esto se explica más adelante en una de las respuestas a una pregunta relacionada: ¿Por qué tenemos que usar el interruptor de interrupción? Así que terminamos con el caso de que necesita una declaración de interrupción para decirle a su programa que ha terminado de ejecutar este bloque. Podría usar break en otros tipos de declaraciones, como un bucle for o while de manera similar.

En lenguajes como java que usan cortocircuito, en un bloque if-elseif-else, si la condición para el primer caso de la instrucción if es verdadera, las condiciones del bloque else if y else no se evaluarán. Después de un si se espera que encuentre un bloque de código entre llaves {} o una sola declaración. Por definición, si no significa "lo contrario" o si la condición anterior no era cierta, sería un poco contradictorio dejar que el código no se cumpliera.

Jessica Brown
fuente
"Aunque pensar que un bloque 'if-else' es como una 'declaración de cambio' sin interrupción podría ser una abstracción útil para un nuevo programador, no es una comparación perfecta". - ¿No te refieres a "pensar en un bloque 'if-else' como una 'declaración de cambio' con un descanso ...?"
Chris Okyen
Contradictorio es correcto!
Chris Okyen
1

Los lenguajes de programación a menudo proporcionan muchas construcciones diferentes que le permiten expresar una lógica similar utilizando una sintaxis diferente.

if/ elsedeclaraciones no "caen" entre sí incondicionalmente; la palabra clave else evita que else-ifse evalúen otras condiciones justo cuando un breakal final de un bloque de casos lo saca de un interruptor, sin embargo, crear un conjunto de ifs independientes (sin usar else) hará que cada ifcondición se evalúe por sí misma.

De manera encubierta, eliminar la breakpalabra clave del final de sus casebloques hará que el flujo de ejecución continúe cayendo incondicionalmente hasta el siguiente caso hasta que se encuentre una interrupción / retorno / goto o se haya alcanzado el final del bloque del interruptor.

Hay (raramente) situaciones en las que se considera útil la interrupción de cambio de mayúsculas y minúsculas; sin embargo, es más común y, por lo general, más idiomático querer que cada bloque de casos se trate solo.

if-elsey switch-caseson dos construcciones que le permiten expresar la selección (toma de decisiones) en un lenguaje de programación; A veces, la elección entre los dos se ha reducido a un estilo personal, otras veces hay razones de peso para elegir uno sobre el otro - Existe una decisión similar cuando se elige para la web frente , mientras que para expresar la repetición.

Ben Cottrell
fuente
Gracias. Un ejemplo de situaciones en las que se considera que el cambio de mayúsculas y minúsculas se considera útil es cuando desea que un caso haga algo y posiblemente permita que suceda algo más después de un resultado verdadero para ese caso si el siguiente caso también es verdadero ... interruptor (i) caso: A {descanso; } caso: B {....} caso: C {.... descanso} Entonces, si B ocurre y luego permite la posibilidad de que ocurra C ...
Chris Okyen
0

La respuesta es no: la instrucción if no es como una instrucción switch sin interrupción. Comencemos con esta parte de su pregunta:

si boolean_expression_1 fuera verdadero, ¿comprobaría si boolean_expression_2 es ​​verdadero?

Podemos encontrar la respuesta con este programa. El truco es que llamamos a un método como nuestra expresión booleana y usamos System.out.println () para ver si el método se ejecuta. De esta manera sabemos si se evaluó la expresión. Aquí está el 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");
    }

}

Verá que la segunda expresión booleana no se evalúa. Este comportamiento probablemente se vuelve más comprensible si coloca algunos corchetes (lo cual es un buen hábito en la mayoría de los 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");
        }
    }

De esta manera, el compilador ve su cadena if original. Como puede ver ahora, el segundo "if" es una declaración única y, como tal, no está en el mismo nivel que la primera declaración if. Si encadena más sentencias if, estas se anidan aún más profundamente.

También preguntó por qué la declaración de cambio necesita una declaración de interrupción. Esto se debe a que la instrucción switch no se implementa internamente con una cadena de instrucciones if (ni la instrucción if se basa en instrucciones switch). Puedes esperar que

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

se traduce por el compilador en algo como:

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

Este no es el caso.En su lugar, utiliza una declaración de omisión 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");

El método numberOfStatementsToSkipFor () devuelve 0 para 42, 1 para 43 y 2 para todo lo demás. Ahora queda claro por qué este programa podría producir

A
B
z

o

B
z

o solo

z

pero nunca

A
B

sin la 'z'. Pero claramente sería bueno si pudiéramos hacer esto. Y podemos usar break que se traduce en otra declaración de omisión. Entonces, si pones un salto en todas las ramas de casos

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

el compilador producirá esto:

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
fuente
Gracias, entendí todo hasta 'int x = numberOfStatementsToSkipFor (controllerExpression ());' fragmento de código que describe cómo omitirá el siguiente número x de instrucciones de código de bytes si tiene una instrucción de cambio como lo mostró. ¿El compilador convierte la instrucción switch en código que le dice que omita X # de bytecode y la JVM lo hace?
Chris Okyen
Además, no entiendo cómo funciona este proceso de omisión ... Conozco algunos asm M6800, ¿es como saltar el puntero a la cantidad de X de la pila de llamadas ... No todas las declaraciones de los 2 casos y el valor predeterminado se llaman así ¿Por qué se los saltearían? ¿Cómo sabe omitirlos? ¿Y por qué pusiste omitir solo las declaraciones (o acciones) llamadas debido a que los casos son verdaderos ... ¿No es necesario omitir el código de cambio {y caso: código y valor predeterminado: y} o no es real? "opcode" pero solo lógica puesta en lenguajes de programación de nivel superior como C y Java y Pascal?
Chris Okyen
@ChrisOkyen Intentaba ilustrar el concepto básico. La realización real en la JVM se describe aquí: artima.com/underthehood/flowP.html
scarfridge
0

Para responder la pregunta editada (con los descansos)

Evaluarían las declaraciones de la misma manera, si y solo si las condiciones para ingresar a cada bloque son las mismas.

Para responder a la pregunta subjetiva, de

da un ángulo de pensamiento de las similitudes conservadas y también lo que son (diferentes / iguales) en el fondo

Comparar los dos te distrae de lo que cada uno fue diseñado para expresar rápidamente.

A switchcentraliza sintácticamente la comparación para que pueda seguir el flujo en función de sus casos.

Mientras que una ifdescribe una sola condición para entrar en un bloque.

Básicamente, el código simplemente "lee" de manera diferente.

Por ejemplo, tienes un worden tu cabeza y quieres buscarlo en el diccionario.

Un interruptor se leería así: sostenga la letra inicial en su cabeza y luego pase el pulgar por el alfabeto, luego ríndase si no comienza con esa letra.

Mientras que un montón de bloques if-else se leerían como: ¿la palabra comienza con la letra? repetido muchas veces, luego rendirse.

Si está tratando de explicar cómo funciona un interruptor a un nuevo programador (que está familiarizado con los bloques if, podría ser más fácil explicarlo como:

if( boolean_expression_1 ) {
  statement_1;
  return;
}

if( boolean_expression_2 ) {
  statement_2;
  return;
}

default_statement;
return;

Ahora, el ejemplo anterior rompe lo que se enseña típicamente en las escuelas, pero regresar temprano y regresar a menudo es a veces más fácil de leer, e igual de rápido en una máquina virtual basada en pila.

Debería haber optimizaciones para los interruptores en primitivas, por lo que la elección de qué bloque de control usar se debe basar en lo que está comparando.

Recuerde que las declaraciones largas switchy extensas iftienden a volverse muy detalladas, difíciles de mantener y también degradan el rendimiento, ya que tendrían que verificarse más condiciones.

Es posible que desee considerar una Hashde las clases / funciones anónimas o un patrón de comando, en lugar de bloques de control largos con muchas condiciones.

El patrón de comandos y hashes permite que el código funcione más rápido y mejora la capacidad de mantenimiento del código.

Brian
fuente