stringtranslate.com

Evaluación de cortocircuito

La evaluación de cortocircuito , evaluación mínima o evaluación McCarthy (en honor a John McCarthy ) es la semántica de algunos operadores booleanos en algunos lenguajes de programación en los que el segundo argumento se ejecuta o evalúa solo si el primer argumento no es suficiente para determinar el valor del expresión: cuando el primer argumento de la ANDfunción se evalúa como false, el valor general debe ser false; y cuando el primer argumento de la ORfunción se evalúa como true, el valor general debe ser true.

En lenguajes de programación con evaluación diferida ( Lisp , Perl , Haskell ), los operadores booleanos habituales provocan un cortocircuito. En otros ( Ada , Java , Delphi ), están disponibles operadores booleanos estándar y de cortocircuito. Para algunas operaciones booleanas, como exclusiva o (XOR), es imposible realizar un cortocircuito, porque siempre se necesitan ambos operandos para determinar un resultado.

Los operadores de cortocircuito son, en efecto, estructuras de control más que simples operadores aritméticos, ya que no son estrictos . En términos de lenguaje imperativo (notablemente C y C++ ), donde los efectos secundarios son importantes, los operadores de cortocircuito introducen un punto de secuencia : evalúan completamente el primer argumento, incluidos los efectos secundarios , antes (opcionalmente) de procesar el segundo argumento. ALGOL 68 utilizó procedimientos para lograr operadores y procedimientos de cortocircuito definidos por el usuario .

El uso de operadores de cortocircuito ha sido criticado por ser problemático:

Los conectivos condicionales (“ cand ” y “ cor ” para abreviar) son... menos inocentes de lo que podrían parecer a primera vista. Por ejemplo, cor no se distribuye sobre cand : compare

(A y B) cor C con (A y B) y (B y C);

en el caso ¬A ∧ C , la segunda expresión requiere que B esté definida, la primera no. Debido a que los conectivos condicionales complican el razonamiento formal sobre los programas, es mejor evitarlos.

Definición

En cualquier lenguaje de programación que implemente la evaluación de cortocircuito, la expresión es equivalente a la expresión condicional y la expresión es equivalente a . En cualquier caso, x sólo se evalúa una vez.x and y if x then y else xx or yif x then x else y

La definición generalizada anterior se adapta a lenguajes de tipo flexible que tienen más de dos valores de verdad True y False, donde los operadores de cortocircuito pueden devolver la última subexpresión evaluada. Esto se denomina "último valor" en la siguiente tabla. Para un lenguaje estrictamente tipado, la expresión se simplifica a y respectivamente para el caso booleano.if x then y else falseif x then true else y

Precedencia

Aunque ANDtiene prioridad en ORmuchos idiomas, esta no es una propiedad universal de la evaluación de cortocircuito. Un ejemplo de dos operadores que tienen la misma prioridad y son asociativos por izquierda entre sí es la sintaxis de la lista de comandos del shell POSIX . [2] : §2.9.3 

El siguiente evaluador simple de izquierda a derecha impone una precedencia de ANDover ORpor a continue:

función evaluación-cortocircuito ( operadores , valores ) let  resultado  := Verdadero para cada ( op , val ) en ( operadores , valores ): si  op = "Y" && resultado = Falso continuar  si no  op = "O" && resultado = Resultado devuelto verdadero resultado de lo contrario  : = resultado devuelto val    

Formalización

La lógica de cortocircuito, con o sin efectos secundarios, se ha formalizado basándose en el condicional de Hoare . Un resultado es que se pueden definir operadores que no provocan cortocircuitos a partir de la lógica de cortocircuito para que tengan la misma secuencia de evaluación. [3]

Soporte en lenguajes de programación y scripting comunes.

Al observar la siguiente tabla, tenga en cuenta que los operadores bit a bit a menudo no se comportan exactamente como operadores lógicos, incluso si ambos argumentos son de tipo booleano o 0.1

Ejemplos:

  1. ^ ab ABAP y APL no tienen un tipo booleano distinto.
  2. ^ Los operadores bit a bit se comportan como operadores booleanos cuando ambos argumentos son de tipo boolo toman solo los valores 0o 1. [4]
  3. ^ Cuando se sobrecarga , los operadores &&y ||están ansiosos y pueden devolver cualquier tipo.
  4. ^ Esto solo se aplica a expresiones evaluadas en tiempo de ejecución static ify static assert. Las expresiones en inicializadores estáticos o constantes manifiestas utilizan una evaluación entusiasta.
  5. ^ Los operadores de Fortran no son cortocircuitados ni ansiosos: la especificación del lenguaje permite al compilador seleccionar el método de optimización.
  6. ^ ISO/IEC 10206:1990 Pascal extendido permite, pero no requiere, cortocircuitos.
  7. ^ ab Delphi y Free Pascal utilizan de forma predeterminada la evaluación de cortocircuito. Esto puede cambiarse mediante las opciones del compilador , pero no parece usarse ampliamente.
  8. ^ Smalltalk utiliza semántica de cortocircuito siempre que el argumento and:sea un bloque (p. ej., false and: [Transcript show: 'Wont see me']).
  9. ^ La norma IEC 61131-3 en realidad no define si ANDse ORutiliza evaluación de cortocircuito y no define los operadores AND_THENy OR_ELSE. Las entradas de la tabla muestran cómo funciona Beckhoff TwinCAT®.
  10. ^ Los lenguajes BÁSICOS que admitían declaraciones CASE lo hacían utilizando el sistema de evaluación condicional, en lugar de tablas de salto limitadas a etiquetas fijas.

Uso común

Evitar efectos secundarios no deseados del segundo argumento.

Ejemplo habitual, utilizando un lenguaje basado en C :

int denominación = 0 ; if ( denom != 0 && num / denom ) { ... // garantiza que el cálculo de num/denom nunca resulte en un error de división por cero }            

Considere el siguiente ejemplo:

int a = 0 ; if ( a != 0 && mifunc ( b )) { hacer_algo (); }         

En este ejemplo, la evaluación de cortocircuito garantiza que myfunc(b)nunca se llame. Esto se debe a que a != 0su evaluación es falsa . Esta característica permite dos construcciones de programación útiles.

  1. Si la primera subexpresión verifica si se necesita un cálculo costoso y la verificación se evalúa como falsa , se puede eliminar el cálculo costoso en el segundo argumento.
  2. Permite una construcción donde la primera expresión garantiza una condición sin la cual la segunda expresión puede causar un error de tiempo de ejecución .

Ambos se ilustran en el siguiente fragmento de C, donde una evaluación mínima evita tanto la desreferencia del puntero nulo como el exceso de recuperación de memoria:

bool is_first_char_valid_alpha_unsafe ( const char * p ) { return isalpha ( p [ 0 ]); // SEGFAULT muy posible con p == NULL }      bool is_first_char_valid_alpha ( const char * p ) { return p != NULL && isalpha ( p [ 0 ]); // 1) no se ejecuta isalpha() innecesaria con p == NULL, 2) no hay riesgo SEGFAULT }          

Construcción condicional idiomática

Dado que la evaluación mínima es parte de la definición semántica de un operador y no una optimización opcional , varios modismos de codificación se basan en ella como una construcción condicional sucinta. Ejemplos incluyen:

Modismos de Perl :

alguna_condición o morir ; # Cancelar la ejecución si alguna_condición es falsa alguna_condición y muere ; # Cancelar la ejecución si alguna_condición es verdadera      

Modismos de shell POSIX : [13]

modprobe  -q  algún_módulo && echo "algún_módulo instalado" || echo "algún_módulo no instalado"      

Este modismo supone que echono puede fallar.

Posibles problemas

Una segunda condición no probada conduce a un efecto secundario no realizado

A pesar de estos beneficios, una evaluación mínima puede causar problemas a los programadores que no se dan cuenta (u olvidan) lo que está sucediendo. Por ejemplo, en el código

if ( expresiónA && mifunc ( b )) { hacer_algo (); }     

Si myfunc(b)se supone que debe realizar alguna operación requerida independientemente de si do_something()se ejecuta, como asignar recursos del sistema, y expressionA​​se evalúa como falsa, entonces myfunc(b)no se ejecutará, lo que podría causar problemas. Algunos lenguajes de programación, como Java , tienen dos operadores, uno que emplea una evaluación mínima y otro que no, para evitar este problema.

Los problemas con declaraciones de efectos secundarios no realizadas se pueden resolver fácilmente con un estilo de programación adecuado, es decir, sin utilizar efectos secundarios en declaraciones booleanas, ya que el uso de valores con efectos secundarios en las evaluaciones tiende a hacer que el código sea opaco y propenso a errores. [14]

Eficiencia reducida debido a optimizaciones restrictivas.

Los cortocircuitos pueden provocar errores en la predicción de ramas en las unidades centrales de procesamiento (CPU) modernas y reducir drásticamente el rendimiento. Un ejemplo notable es el código de intersección de cuadros alineados con ejes altamente optimizado en el trazado de rayos . [ se necesita aclaración ] Algunos compiladores pueden detectar estos casos y emitir código más rápido, pero la semántica del lenguaje de programación puede limitar dichas optimizaciones. [ cita necesaria ]

Un ejemplo de un compilador que no puede optimizar para tal caso es la máquina virtual (VM) Hotspot de Java a partir de 2012. [15]

Ver también

Referencias

  1. ^ Edsger W. Dijkstra "Sobre una correspondencia algo decepcionante", EWD1009-0, 25 de mayo de 1987 texto completo
  2. ^ "Lenguaje de comando de Shell". pubs.opengroup.org .
  3. ^ Bergstra, enero A.; Ponse, A.; Staudt, DJC (2010). "Lógica de cortocircuito". arXiv : 1010.3674 [cs.LO].
  4. ^ Norma ISO/IEC 9899, ​​secciones 6.2.5, 6.3.1.2, 6.5 y 7.16.
  5. ^ Norma ISO/IEC 9899, ​​sección 6.5.13
  6. ^ Borrador ISO / IEC IS 14882.
  7. ^ "OCaml: el lenguaje OCaml".
  8. ^ "operadores - Documentación para Ruby 3.3". docs.ruby-lang.org . Consultado el 2 de abril de 2024 .
  9. ^ "std::ops - Óxido". doc.rust-lang.org . Consultado el 12 de febrero de 2019 .
  10. ^ ETSI ES 201873-1 V4.10.1, sección 7.1.4
  11. ^ "Sistema de información Beckhoff - Inglés". infosys.beckhoff.com . Consultado el 16 de agosto de 2021 .
  12. ^ "Sistema de información Beckhoff - Inglés". infosys.beckhoff.com . Consultado el 16 de agosto de 2021 .
  13. ^ "¿Qué significa || en bash?". stackexchange.com . Consultado el 9 de enero de 2019 .
  14. ^ "Transparencia referencial, precisión y capacidad de despliegue" (PDF) . Itu.dk. ​Consultado el 24 de agosto de 2013 .
  15. ^ Wasserman, Louis (11 de julio de 2012). "Java: Cuáles son los casos en los que es mejor usar AND incondicional (& en lugar de &&)". Desbordamiento de pila .