stringtranslate.com

Evaluación de cortocircuito

La evaluación de cortocircuito , evaluación mínima o evaluación de 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 de la 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 se cortocircuitan. En otros ( Ada , Java , Delphi ), están disponibles tanto operadores booleanos estándar como de cortocircuitación. Para algunas operaciones booleanas, como la operación exclusiva o (XOR), es imposible cortocircuitar, porque siempre se necesitan ambos operandos para determinar un resultado.

Los operadores de cortocircuito son, en efecto, estructuras de control en lugar de simples operadores aritméticos, ya que no son estrictos . En términos de lenguaje imperativo (en particular 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 de procesar (opcionalmente) 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 conectores 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 cand B) cor C con (A cor C) cand (B cor C);

En el caso ¬A ∧ C , la segunda expresión requiere que B esté definido, la primera no. Como los conectores condicionales complican el razonamiento formal sobre los programas, es mejor evitarlos.

Definición

En cualquier lenguaje de programación que implemente la evaluación por cortocircuito, la expresión es equivalente a la expresión condicional y la expresión es equivalente a . En cualquier caso, x solo 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 los lenguajes de tipado 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 tabla siguiente. Para un lenguaje de tipado estricto, 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 muchos lenguajes, no es una propiedad universal de la evaluación de ORcortocircuito. Un ejemplo de los dos operadores que tienen la misma prioridad y son asociativos por la izquierda entre sí es la sintaxis de lista de comandos del shell POSIX . [2] : §2.9.3 

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

función cortocircuito-eval( operadores , valores ) deja  resultado  := Verdadero para cada ( op , val ) en ( operadores , valores ): si  op = "AND" && resultado = Falso continuar  de lo contrario si  op = "OR" && resultado = Verdadero devolver  resultado  de lo contrario  resultado  := val  devolver  resultado

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 los operadores que no generan cortocircuito se pueden definir 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 0, 1o booleano.

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 del tipo boolo toman solo los valores 0o 1. [4]
  3. ^ Cuando hay 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 de manifiesto utilizan una evaluación diligente.
  5. ^ Los operadores de Fortran no son ni cortocircuito ni ansiosos: la especificación del lenguaje permite al compilador seleccionar el método para la optimización.
  6. ^ ISO/IEC 10206:1990 Extended Pascal permite, pero no requiere, el cortocircuito.
  7. ^ ab Delphi y Free Pascal utilizan de forma predeterminada la evaluación por cortocircuito. Esto se puede modificar mediante opciones del compilador , pero no parece que se utilice ampliamente.
  8. ^ Smalltalk utiliza semántica de cortocircuito siempre que el argumento and:sea un bloque (por ejemplo, false and: [Transcript show: 'Wont see me']).
  9. ^ La norma IEC 61131-3 no define si ANDse ORutiliza la evaluación de cortocircuito y no define los operadores AND_THENy OR_ELSE. Las entradas de la tabla muestran cómo funciona para Beckhoff TwinCAT®.
  10. ^ Los lenguajes BASIC 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

Cómo evitar los efectos secundarios no deseados del segundo argumento

Ejemplo habitual, utilizando un lenguaje basado en C :

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

Consideremos el siguiente ejemplo:

int a = 0 ; si ( a != 0 && mifunción ( b )) { hacer_algo (); }         

En este ejemplo, la evaluación de cortocircuito garantiza que myfunc(b)nunca se llame a . Esto se debe a que a != 0evalúa como falso . 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 evalúa como falso , 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 en tiempo de ejecución .

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

bool is_first_char_valid_alpha_unsafe ( const char * p ) { return isalpha ( p [ 0 ]); // ERROR DE SEGMENTACIÓN es muy posible con p == NULL }      bool is_first_char_valid_alpha ( const char * p ) { return p != NULL && isalpha ( p [ 0 ]); // 1) no hay ejecución innecesaria de isalpha() con p == NULL, 2) no hay riesgo de SEGFAULT }          

Constructo condicional idiomático

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. Algunos ejemplos incluyen:

Modismos de Perl :

some_condition o die ; # Abortar la ejecución si some_condition es falsa some_condition y die ; # Abortar la ejecución si some_condition es verdadera      

Modismos del shell POSIX : [13]

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

Este modismo presupone que echono puede fallar.

Posibles problemas

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

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

si ( expresiónA && mifunción ( 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 falso, 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 evaluación mínima y otro que no, para evitar este problema.

Los problemas con declaraciones de efectos secundarios no ejecutados se pueden resolver fácilmente con un estilo de programación adecuado, es decir, no utilizando 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 generalmente opaco y propenso a errores. [14]

Eficiencia reducida debido a optimizaciones restrictivas

Los cortocircuitos pueden provocar errores en la predicción de ramificaciones 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 caja alineada con el eje de un rayo altamente optimizado en el trazado de rayos . [ Aclaración necesaria ] 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 estas optimizaciones. [ Cita requerida ]

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

Véase también

Referencias

  1. ^ Edsger W. Dijkstra "Sobre una correspondencia algo decepcionante", EWD1009-0, 25 de mayo de 1987 texto completo
  2. ^ "Lenguaje de comandos 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, ​​apartado 6.5.13
  6. ^ Borrador de 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 - Rust". 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 de Beckhoff - Inglés". infosys.beckhoff.com . Consultado el 16 de agosto de 2021 .
  12. ^ "Sistema de información de 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 desdoblabilidad" (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 utilizar AND incondicional (& en lugar de &&)". Desbordamiento de pila .