stringtranslate.com

Declaración de cambio

En los lenguajes de programación de computadoras , una declaración de cambio es un tipo de mecanismo de control de selección que se utiliza para permitir que el valor de una variable o expresión cambie el flujo de control de la ejecución del programa mediante búsqueda y mapa.

Las declaraciones de cambio funcionan de manera algo similar a las ifdeclaraciones utilizadas en lenguajes de programación como C / C++ , C# , Visual Basic .NET , Java y existen en la mayoría de los lenguajes de programación imperativos de alto nivel como Pascal , Ada , C / C++ , C# , [1] : 374–375  Visual Basic .NET , Java , [2] : 157–167  y en muchos otros tipos de lenguaje, utilizando palabras clave como switch, caseo select.inspect

Las sentencias switch vienen en dos variantes principales: un switch estructurado, como en Pascal, que toma exactamente una rama, y ​​un switch no estructurado, como en C, que funciona como un tipo de goto . Las razones principales para usar un conmutador incluyen mejorar la claridad, al reducir la codificación que de otro modo sería repetitiva y (si la heurística lo permite) también ofrecer el potencial de una ejecución más rápida a través de una optimización del compilador más sencilla en muchos casos.

Historia

En su texto de 1952 Introducción a las metamatemáticas , Stephen Kleene demostró formalmente que la función CASO (siendo la función SI-ENTONCES-ELSE su forma más simple) es una función recursiva primitiva , donde define la noción definition by casesde la siguiente manera:

"#F. La función φ definida así
φ(x 1 , ... , x norte ) =
  • φ 1 (x 1 , ... , x n ) si Q 1 (x 1 , ... , x n ),
  • . . . . . . . . . . . .
  • φ m (x 1 , ... , x n ) si Q m (x 1 , ... , x n ),
  • φ m+1 (x 1 , ... , x n ) en caso contrario,
donde Q 1 , ... , Q m son predicados mutuamente excluyentes (o φ(x 1 , ... , x n ) tendrá el valor dado por la primera cláusula que se aplica) es primitiva recursiva en φ 1 , ... , φ m+1 , Q 1 , ..., Q m+1 . [3]

Kleene proporciona una prueba de esto en términos de las funciones recursivas de tipo booleano "signo de" sg() y "no signo de" ~sg() (Kleene 1952:222-223); el primero devuelve 1 si su entrada es positiva y −1 si su entrada es negativa.

Boolos-Burgess-Jeffrey hace la observación adicional de que la "definición por casos" debe ser mutuamente excluyente y colectivamente exhaustiva . También ofrecen una prueba de la recursividad primitiva de esta función (Boolos-Burgess-Jeffrey 2002:74-75).

IF-THEN-ELSE es la base del formalismo McCarthy : su uso reemplaza tanto la recursividad primitiva como el operador mu .

Sintaxis típica

En la mayoría de los lenguajes, los programadores escriben una declaración de cambio en muchas líneas individuales usando una o dos palabras clave. Una sintaxis típica implica:

Cada alternativa comienza con el valor particular, o lista de valores (ver más abajo), que la variable de control puede coincidir y que hará que el control pase a la secuencia correspondiente de declaraciones. El valor (o lista/rango de valores) generalmente está separado de la secuencia de declaración correspondiente por dos puntos o por una flecha de implicación. En muchos idiomas, cada caso debe ir precedido de una palabra clave como caseo when.

Por lo general, también se permite un caso predeterminado opcional, especificado mediante una palabra clave default, otherwiseo else. Esto se ejecuta cuando ninguno de los otros casos coincide con la expresión de control. En algunos lenguajes, como C, si ningún caso coincide y se defaultomite, la switchdeclaración simplemente no hace nada. En otros, como PL/I, se genera un error.

Semántica

Semánticamente, existen dos formas principales de declaraciones de cambio.

La primera forma son conmutadores estructurados, como en Pascal, donde se toma exactamente una rama y los casos se tratan como bloques separados y exclusivos. Esto funciona como un condicional if-then-else generalizado , aquí con cualquier número de ramas, no solo dos.

La segunda forma son conmutadores no estructurados, como en C, donde los casos se tratan como etiquetas dentro de un solo bloque y el conmutador funciona como un goto generalizado. Esta distinción se conoce como el tratamiento del fracaso, que se detalla a continuación.

Caer a través

En muchos idiomas, solo se ejecuta el bloque coincidente y luego la ejecución continúa al final de la declaración de cambio. Estos incluyen la familia Pascal (Object Pascal, Modula, Oberon, Ada, etc.), así como PL/I , formas modernas de dialectos Fortran y BASIC influenciados por Pascal, la mayoría de los lenguajes funcionales y muchos otros. Para permitir que múltiples valores ejecuten el mismo código (y evitar la necesidad de duplicar el código ), los lenguajes de tipo Pascal permiten cualquier número de valores por caso, dados como una lista separada por comas, como un rango o como una combinación.

Los lenguajes derivados del lenguaje C, y más generalmente aquellos influenciados por el GOTO calculado de Fortran , en su lugar presentan falla, donde el control se mueve al caso coincidente y luego la ejecución continúa ("falla") hasta las declaraciones asociadas con el siguiente caso en el texto fuente. . Esto también permite que varios valores coincidan con el mismo punto sin ninguna sintaxis especial: simplemente se enumeran con cuerpos vacíos. Los valores pueden tener condiciones especiales con código en el cuerpo del caso. En la práctica, la falla generalmente se evita con una breakpalabra clave al final del cuerpo coincidente, que sale de la ejecución del bloque de cambio, pero esto puede causar errores debido a fallas involuntarias si el programador olvida insertar la breakdeclaración. Por lo tanto, muchos [4] ven esto como una verruga del lenguaje y algunas herramientas de pelusa advierten contra ello. Sintácticamente, los casos se interpretan como etiquetas, no como bloques, y las declaraciones switch y break cambian explícitamente el flujo de control. Algunos lenguajes influenciados por C, como JavaScript , conservan la falla predeterminada, mientras que otros la eliminan o solo la permiten en circunstancias especiales. Las variaciones notables de esto en la familia C incluyen C# , en el que todos los bloques deben terminar con breako returna menos que el bloque esté vacío (es decir, la opción alternativa se utiliza como una forma de especificar múltiples valores).

En algunos casos, los idiomas ofrecen una alternativa opcional. Por ejemplo, Perl no falla de forma predeterminada, pero un caso puede hacerlo explícitamente usando una continuepalabra clave. Esto evita caídas involuntarias, pero lo permite cuando se desea. De manera similar, Bash por defecto no falla cuando termina con ;;, pero permite la falla [5] con ;&o ;;&en su lugar.

Un ejemplo de una declaración de cambio que se basa en la falla es el dispositivo de Duff .

Compilacion

Los compiladores optimizadores como GCC o Clang pueden compilar una declaración de cambio en una tabla de rama o en una búsqueda binaria a través de los valores en los casos. [6] Una tabla de bifurcación permite que la instrucción de cambio determine con un número pequeño y constante de instrucciones qué bifurcación ejecutar sin tener que pasar por una lista de comparaciones, mientras que una búsqueda binaria toma solo un número logarítmico de comparaciones, medido en el número de casos en la declaración de cambio.

Normalmente, el único método para averiguar si se ha producido esta optimización es observando la salida resultante del código ensamblador o de máquina que ha generado el compilador.

Ventajas y desventajas

En algunos lenguajes y entornos de programación, el uso de una declaración caseo switchse considera superior a una serie equivalente de declaraciones if else if porque es:

Además, una implementación optimizada puede ejecutarse mucho más rápido que la alternativa, porque a menudo se implementa mediante una tabla de rama indexada . [7] Por ejemplo, decidir el flujo del programa basándose en el valor de un solo carácter, si se implementa correctamente, es mucho más eficiente que la alternativa, reduciendo considerablemente la longitud de la ruta de instrucción . Cuando se implementa como tal, una declaración de cambio se convierte esencialmente en un hash perfecto .

En términos del gráfico de flujo de control , una declaración de cambio consta de dos nodos (entrada y salida), más un borde entre ellos para cada opción. Por el contrario, una secuencia de declaraciones "if...else if...else if" tiene un nodo adicional para cada caso excepto el primero y el último, junto con un borde correspondiente. El gráfico de flujo de control resultante para las secuencias de "si" tiene, por tanto, muchos más nodos y casi el doble de aristas, que no añaden ninguna información útil. Sin embargo, las ramas simples en las declaraciones if son conceptualmente más fáciles individualmente que la rama compleja de una declaración switch. En términos de complejidad ciclomática , ambas opciones la aumentan en k −1 si se dan k casos.

Cambiar expresiones

Las expresiones de cambio se introducen en Java SE 12 , el 19 de marzo de 2019, como función de vista previa. Aquí se puede utilizar una expresión de cambio completa para devolver un valor. También hay una nueva forma de etiqueta de caso, case L->donde el lado derecho es una expresión única. Sin embargo, esto también evita caídas y requiere que los casos sean exhaustivos. En Java SE 13 yieldse introduce la declaración y en Java SE 14 las expresiones de cambio se convierten en una característica del lenguaje estándar. [8] [9] [10] Por ejemplo:

int ndays = switch ( mes ) { case ENERO , MARZO , MAYO , JULIO , AGO , OCTUBRE , DICIEMBRE -> 31 ; caso ABR , JUN , SEP , NOV -> 30 ; caso FEB -> { if ( año % 400 == 0 ) rendimiento 29 ; de lo contrario, si ( año % 100 == 0 ) rinde 28 ; de lo contrario, si ( año % 4 == 0 ) rinde 29 ; de lo contrario, rinda 28 ; } };                                                        

Usos alternativos

Muchos lenguajes evalúan expresiones dentro de switchbloques en tiempo de ejecución, lo que permite una serie de usos menos obvios para la construcción. Esto prohíbe ciertas optimizaciones del compilador, por lo que es más común en lenguajes dinámicos y de secuencias de comandos donde la flexibilidad mejorada es más importante que la sobrecarga de rendimiento.

PHP

Por ejemplo, en PHP , se puede usar una constante como "variable" para verificar, y se ejecutará la primera declaración de caso que evalúe esa constante:

cambiar  ( verdadero )  {  caso  ( $x  ==  'hola' ) :  foo ();  romper ;  caso  ( $z  ==  'hola' ) :  descanso ; } cambiar  ( 5 )  {  caso  $x :  romper ;  caso  $y :  descanso ; }

Esta característica también es útil para comparar múltiples variables con un valor en lugar de una variable con muchos valores. COBOL también admite esta forma (y otras formas) en la EVALUATEdeclaración. PL/I tiene una forma alternativa de SELECTdeclaración donde la expresión de control se omite por completo y se ejecuta la primera WHENque se evalúa como verdadera .

Rubí

En Ruby , debido a su manejo de ===la igualdad, la declaración se puede usar para probar la clase de la variable:

caso de entrada cuando Array luego pone '¡la entrada es una matriz!' cuando Hash luego pone '¡la entrada es un Hash!' fin         

Ruby también devuelve un valor que se puede asignar a una variable y en realidad no requiere que casetenga ningún parámetro (actúa un poco como una else ifdeclaración):

comida para gatos = caso cuando cat . edad <= 1 junior cuando gato . edad > 10 años mayor de lo contrario final normal               

Ensamblador

Una declaración de cambio en lenguaje ensamblador :

cambiar: cmp ah , 00h je a cmp ah , 01h je b jmp swtend ; Ningún caso coincide o código "predeterminado" aquí a: push ah mov al , 'a' mov ah , 0Eh mov bh , 00h int 10h pop ah jmp swtend ; Equivalente a "romper" b: push ah mov al , 'b' mov ah , 0Eh mov bh , 00h int 10h pop ah jmp swtend ; Equivale a "romper" ... swtend:                                                  

Pitón

Para Python 3.10.6, se aceptaron los PEPmatch 634-636, que agregaron palabras clave y case. [11] [12] [13] [14] A diferencia de otros lenguajes, Python en particular no exhibe un comportamiento fallido.

letra  =  entrada ( "Pon una sola letra: " ) . tira ()[ 0 ] . casefold ()  # primer carácter que no sea un espacio en blanco de la entrada, minúscula letra de coincidencia : caso  'a'  |  'mi'  |  'yo'  |  'o'  |  'u' :  # A diferencia de las condiciones en las declaraciones if, la palabra clave `o` no se puede usar aquí para diferenciar entre casos print ( f "¡La letra { letra } es una vocal!" ) caso  'y' : print ( f "La letra { letra } puede ser una vocal." ) case _ : # `case _` es equivalente a `default` de C y otros   print ( f "¡La letra { letra } no es una vocal!" )

Manejo de excepciones

Varios lenguajes implementan una forma de declaración de cambio en el manejo de excepciones , donde si se genera una excepción en un bloque, se elige una rama separada, dependiendo de la excepción. En algunos casos, también está presente una rama predeterminada, si no se genera ninguna excepción. Un ejemplo temprano es Modula-3 , que usa la sintaxis TRY... EXCEPT, donde cada uno EXCEPTdefine un caso. Esto también se encuentra en Delphi , Scala y Visual Basic .NET .

Alternativas

Algunas alternativas a las declaraciones de cambio pueden ser:

(En algunos idiomas, solo se permiten tipos de datos reales como valores en la tabla de búsqueda. En otros idiomas, también es posible asignar funciones como valores de la tabla de búsqueda, obteniendo la misma flexibilidad que una switchdeclaración real. Consulte el artículo sobre la tabla de control para obtener más detalles. en este).
Lua no admite declaraciones case/switch. [15] Esta técnica de búsqueda es una forma de implementar switchdeclaraciones en el lenguaje Lua, que no tiene archivos switch. [15]
En algunos casos, las tablas de búsqueda son más eficientes que las declaraciones no optimizadas switch , ya que muchos lenguajes pueden optimizar las búsquedas en tablas, mientras que las declaraciones de cambio no se optimizan a menos que el rango de valores sea pequeño con pocos espacios. Sin embargo, una búsqueda no binaria y no optimizada será casi con toda seguridad más lenta que un cambio no optimizado o las múltiples sentencias if-else equivalentes. [ cita necesaria ]

Ver también

Referencias

  1. ^ Skeet, Jon (23 de marzo de 2019). C# en profundidad . Manning. ISBN 978-1617294532.
  2. ^ Bloch, Josué (2018). "Java efectivo: Guía del lenguaje de programación" (tercera ed.). Addison-Wesley. ISBN 978-0134685991.
  3. ^ "Definición por casos", Kleene 1952:229
  4. ^ van der Linden, Peter (1994). Programación experta en C: secretos profundos de C , p. 38. Prentice Hall, Acantilados de Eaglewood. ISBN 0131774298
  5. ^ desde la versión 4.0, lanzada en 2009.
  6. ^ Vlad Lázarenko. De la declaración de cambio al código de máquina
  7. ^ Guntheroth, Kurt (27 de abril de 2016). C++ optimizado . Medios O'Reilly. pag. 182.ISBN _ 9781491922033.
  8. ^ "JEP 325: Cambiar expresiones (vista previa)". openjdk.java.net . Consultado el 28 de abril de 2021 .
  9. ^ "JEP 354: Cambiar expresiones (segunda vista previa)". openjdk.java.net . Consultado el 28 de abril de 2021 .
  10. ^ "JEP 361: Cambiar expresiones". openjdk.java.net . Consultado el 28 de abril de 2021 .
  11. ^ Galindo Salgado, Pablo. "Novedades de Python 3.10". Documentación de Python 3.10.6 . Consultado el 19 de agosto de 2022 .
  12. ^ Bucher, Brandt; van Rossum, Guido (12 de septiembre de 2020). "PEP 634 - Coincidencia de patrones estructurales: especificación". Propuestas de mejora de Python . Consultado el 19 de agosto de 2022 .
  13. ^ Kohn, Tobías ; van Rossum, Guido (12 de septiembre de 2020). "PEP 635 - Coincidencia de patrones estructurales: motivación y justificación". Propuestas de mejora de Python . Consultado el 19 de agosto de 2022 .
  14. ^ Moisset, Daniel F. "PEP 636 - Coincidencia de patrones estructurales: tutorial". Propuestas de mejora de Python . Consultado el 19 de agosto de 2022 .
  15. ^ ab Declaración de cambio en Lua

Otras lecturas