Operador relacional

De Wikipedia, la enciclopedia libre

En ciencias de la computación, un operador relacional es una construcción de un lenguaje de programación o un operador que comprueba o define algún tipo de relación entre dos entidades. Incluyen la igualdad numérica (como por ejemplo, 5 = 5) y las desigualdades (como por ejemplo, 4 ≥ 3).

En los lenguajes de programación que incluyen un tipo de dato lógico distinto en su sistema de tipos, como Pascal, Ada o Java, estos operadores generalmente se evalúan como verdadero o falso, dependiendo de si la relación condicional entre los dos operandos se cumple o no. En lenguajes como C, los operadores relacionales devuelven los números enteros 0 o 1, donde 0 representa falso y cualquier valor distinto de cero representa verdadero.

Un expresión creada utilizando un operador relacional forma lo que se denomina una expresión relacional o una condición. Los operadores relacionales pueden verse como casos especiales de predicados lógicos.

Igualdad[editar]

Uso[editar]

La igualdad se utiliza en muchas construcciones de lenguajes de programación y tipos de datos. Se emplea para probar si un elemento ya existe en un conjunto, o para acceder a un valor a través de una clave. Se utiliza en selección de casos para enviar el flujo de control a la rama correcta y durante el proceso de unificación en programación lógica.

Un posible significado de la igualdad es que "si a es igual a b, entonces a o b pueden usarse indistintamente en cualquier contexto sin notar ninguna diferencia". Pero esta afirmación no necesariamente se cumple, particularmente cuando se tiene en cuenta la mutabilidad junto con la igualdad del contenido.

Igualdad de ubicación frente a igualdad de contenido[editar]

A veces, particularmente en programación orientada a objetos, la comparación plantea preguntas sobre tipos de datos y herencia, igualdad e identidad. A menudo es necesario distinguir entre:

  • Dos objetos diferentes del mismo tipo, por ejemplo, dos manos
  • Dos objetos iguales pero distintos, por ejemplo, dos billetes de 10 dólares
  • Dos objetos iguales pero con representación diferente, por ejemplo, un billete de 1 dólar y una moneda de 1 dólar
  • Dos referencias diferentes al mismo objeto, por ejemplo, dos apodos para la misma persona

En muchos lenguajes de programación modernos, se accede a objetos y estructuras de datos a través de referencias. En tales lenguajes, surge la necesidad de comprobar dos tipos diferentes de igualdad:

  • Igualdad de ubicación (identidad): si dos referencias (A y B) hacen referencia al mismo objeto. Las interacciones con el objeto a través de A son indistinguibles de las mismas interacciones a través de B y, en particular, los cambios en el objeto a través de A se reflejan a través de B.
  • Igualdad de contenido: si los objetos referenciados por dos referencias (A y B) son equivalentes en algún sentido:
  • Igualdad estructural (es decir, sus contenidos son iguales) que puede ser superficial (comprobar solo subpartes inmediatas) o profundo (comprobar la igualdad de subpartes de forma recursiva). Una forma sencilla de lograrlo es mediante la igualdad representacional: comprobando que los valores tengan la misma representación.
  • Alguna otra igualdad hecha a medida, preservando el comportamiento externo. Por ejemplo, 1/2 y 2/4 se consideran iguales cuando se consideran números racionales. Un posible requisito sería que "A = B si y solo si todas las operaciones sobre los objetos A y B tendrán el mismo resultado", además de reflexividad, simetría y transitividad.

El primer tipo de igualdad suele implicar el segundo (excepto cosas como "no es un número" (NaN) que no son iguales a sí mismas), pero lo contrario no es necesariamente cierto. Por ejemplo, dos objetos como cadenas de caracteres pueden ser objetos distintos (desiguales en el primer sentido) pero contener la misma secuencia de caracteres (iguales en el segundo sentido). Consúltese identidad para obtener más información sobre este problema.

Los números reales, incluidas muchas fracciones simples, no se pueden representar exactamente en coma flotante y puede ser necesario probar la igualdad dentro de una tolerancia determinada. Sin embargo, dicha tolerancia puede romper fácilmente propiedades deseadas como la transitividad, mientras que la reflexividad también se rompe: el estándar de coma flotante IEEE requiere que "NaN ≠ NaN" se cumpla. Por el contrario, el estándar privado aritmético para úmeros en formato posit (2022) (los promotores de la propuesta pretenden reemplazar los valores de coma flotante IEEE) tiene un concepto similar, NaR (Not a Real), donde se cumple NaR = NaR.[1]

Otros elementos de programación, como las funciones computables, pueden no tener ningún sentido de igualdad o tener una igualdad que no sea computable. Por estas razones, algunos lenguajes definen una noción explícita de "comparable", en forma de clase base, interfaz, rasgo o protocolo, que se utiliza explícitamente, mediante declaración en el código fuente, o implícitamente, a través de la estructura del tipo involucrado.

Comparación de valores de diferentes tipos[editar]

En JavaScript, PHP, VBScript y algunos otros lenguajes escritos dinámicamente, el operador de igualdad estándar se evalúa como "verdadero" si dos valores son iguales, incluso si tienen tipos diferentes, lo que hace que el número 4 se compare con la cadena de texto "4", por ejemplo. En dichos lenguajes también suele estar disponible un operador de igualdad escrito que devuelve verdadero solo para valores con tipos idénticos o equivalentes (en PHP, 4=== "4" es falso aunque 4== "4" es verdadero).[2][3]​ Para lenguajes de programación en los que el número 0 puede interpretarse como "falso", este operador puede simplificar cosas como verificar cero (ya que x== 0 sería verdadero si x fuera tanto 0 como "0", utilizando el operador de igualdad independiente del tipo).

Ordenación[editar]

La comparación "mayor que" y "menor que" de datos no numéricos se realiza de acuerdo con una convención de clasificación (como, para las cadenas de texto, el orden lexicográfico) que puede estar integrada en el lenguaje de programación y/o ser configurable mediante programación.

Cuando se desea asociar un valor numérico con el resultado de una comparación entre dos elementos de datos, digamos a y b, la convención habitual es asignar −1 si a < b, 0 si a = by 1 si a > b. Por ejemplo, la función C cadenas de texto en C realiza una comparación a tres sentidos y devuelve −1, 0 o 1 según esta convención, y qsort espera que la función de comparación devuelva valores según esta convención. En algoritmos de ordenamiento, la eficiencia del código de comparación es fundamental, ya que es uno de los principales factores que contribuyen al rendimiento de la clasificación.

La comparación de tipos de datos definidos por el programador (para los que el lenguaje de programación no tiene comprensión incorporada) se puede llevar a cabo mediante funciones de biblioteca o escritas a medida (como la strcmp mencionada anteriormente) o, en algunos lenguajes, mediante sobrecarga de un operador de comparación, es decir, que asigna un significado definido por el programador que depende de los tipos de datos que se comparan. Otra alternativa es utilizar alguna convención, como la comparación entre miembros.

Equivalencia lógica[editar]

Aunque quizás no sea obvio al principio, como los operadores lógicos boleanos XOR, AND, OR y NOT, los operadores relacionales pueden diseñarse para tener equivalencia lógica, de modo que todos puedan definirse en términos de otros. Las siguientes cuatro declaraciones condicionales tienen la misma equivalencia lógica E (todas verdaderas o todas falsas) para cualquier valor dado de x e y:

Esto depende de que el dominio esté bien ordenado.

Operadores relacionales estándar[editar]

A continuación se muestran los operadores relacionales numéricos más comunes utilizados en los lenguajes de programación. El SQL estándar utiliza los mismos operadores que BASIC, mientras que muchas bases de datos permiten != además del <> del estándar. SQL sigue el álgebra de Boole estricta, es decir, no utiliza evaluación de cortocircuito, que es común a la mayoría de los lenguajes siguientes. Por ejemplo, PHP lo tiene, pero por lo demás dispone de estos mismos dos operadores definidos como alias, como muchas bases de datos SQL.

Operadores relacionales comunes
Convención igual a no igual a mayor que menor que mayor que
o igual a
menor que
o igual a
Símbolo = > <
Fortran[nota 1] .EQ. .NE. .GT. .LT. .GE. .LE.
ALGOL 68[nota 2] = > <
/= >= <=
eq ne gt lt ge le
APL = > <
BASIC, ML, Pascal[nota 3] = <>[nota 4] > < >= <=
Lenguajes tipo C[nota 5] == != > < >= <=
MUMPS = '= > < '< '>
Lua == ~= > < >= <=
Erlang == /= > < >= =<
=:= =/=
Shells del tipo Bourne[nota 6] -eq -ne -gt -lt -ge -le
Archivo batch EQU NEQ GTR LSS GEQ LEQ
MATLAB[nota 7] == ~= > < >= <=
eq(x,y) ne(x,y) gt(x,y) lt(x,y) ge(x,y) le(x,y)
Fortran,[nota 8]Haskell == /= > < >= <=
Mathematica[4] == != > < >= <=
Equal[x,y] Unequal[x,y] Greater[x,y] Less[x,y] GreaterEqual[x,y] LessEqual[x,y]
  1. Incluyendo FORTRAN II, III, IV, 66 y 77.
  2. ALGOL 68: se utilizan textos clave en el código de plataformas con conjuntos de caracteres limitados ("por ejemplo", usando >= o GE en lugar de ), plataformas sin énfasis de texto negrita (se usa 'ge') o plataformas solo con MAYÚSCULA (usando .GE o 'GE').
  3. Incluyendo ALGOL, Simula, Modula-2, Eiffel, SQL, hojas de cálculo, y otros.
  4. Modula-2 también reconoce #
  5. Incluyendo C, C++, C#, Go, Java, JavaScript, Perl (comparación numérica solamente), PHP, Python, Ruby, y R.
  6. Incluyendo Bourne Shell, Bash, Korn shell, y PowerShell. Los símbolos < y > generalmente se usan en un shell para las redirecciones, por lo que se deben usar otros símbolos. Sin el guion, se utiliza en Perl para comparar cadenas.
  7. MATLAB, aunque en otros aspectos usa una sintaxis similar a la de C, no usa !=, ya que ! en MATLAB envía el siguiente texto como una línea de comando al sistema operativo. La primera forma también se usa en Smalltalk, con la excepción de la igualdad, que es =.
  8. Incluyendo FORTRAN 95, 2003, 2008 y 2015.

Otras convenciones son menos comunes: Common Lisp y Macsyma/Maxima usan operadores similares a los de BASIC excepto para la desigualdad, que es /= en Common Lisp y # en Macsyma/Maxima. Los Lisps anteriores usaban equal, greaterp y lessp; y las negaciones usando not para los operadores correspondientes.

Sintaxis[editar]

Los operadores relacionales también se utilizan en la literatura técnica en lugar de palabras. Generalmente se escriben en notación de infijo si son compatibles con el lenguaje de programación, lo que significa que aparecen entre sus operandos (las dos expresiones están relacionadas). Por ejemplo, una expresión en Python imprimirá un mensaje si x es menor que y:

if x < y:
    print("x es menor que y en este ejemplo")

Otros lenguajes de programación, como Lisp, utilizan la notación polaca de la siguiente manera:

(>= X Y)

Operadores encadenados[editar]

En matemáticas, es una práctica común encadenar operadores relacionales, como en 3 < x < y < 20 (es decir, 3 < x y x < y e y < 20). La sintaxis es clara, ya que estos operadores relacionales en matemáticas son transitivos.

Sin embargo, muchos lenguajes de programación recientes verían una expresión como 3 < x < y compuesta por dos operadores asociativos izquierdo (o derecho), interpretándola como algo así como (3 < x) < y. Si se considera que x=4, se obtiene (3 < 4) < y y la evaluación dará true < y, lo que generalmente no tiene sentido. Sin embargo, se compila en C/C++ y algunos otros lenguajes, lo que puede producir resultados sorprendentes (ya que aquí "verdadero" estaría representado por el número 1).

Es posible darle a la expresión x < y < z su significado matemático familiar, y algunos lenguajes de programación como Python y Raku lo hacen. Otros, como C# y Java, no lo hacen, en parte porque diferiría de la forma en que funcionan la mayoría de los otros operadores infijos en los lenguajes tipo C. El D no hace eso, ya que mantiene cierta compatibilidad con C, y "Permitir expresiones en C pero con una semántica sutilmente diferente (aunque posiblemente en la dirección correcta) agregaría más confusión que conveniencia".[5]

Algunos lenguajes, como Common Lisp, utilizan predicados de múltiples argumentos para esto. En Lisp (<= 1 x 10) es verdadero cuando x está entre 1 y 10.

Confusión con los operadores de asignación[editar]

Los primeros FORTRAN (1956–57) estaban limitados por conjuntos de caracteres muy restringidos, en los que = era el único operador relacional disponible. No había < ni > (y ciertamente, ni ni tampoco ). Esto obligó a los diseñadores a definir símbolos como .GT., .LT., .GE., .EQ., etc. y posteriormente hizo tentador usar el carácter = restante para copiar, a pesar de la obvia incoherencia con el uso matemático (X=X+1 debería ser imposible).

El lenguaje algebraico internacional (IAL, ALGOL 58) y ALGOL (1958 y 1960) introdujeron así := para la asignación, dejando el estándar = disponible para la igualdad, una convención seguida por CPL, ALGOL W, ALGOL 68, los lenguajes de programación combinados básicos (BCPL), Simula, Lenguaje SET (SETL), Pascal, Smalltalk, Modula-2, Ada, Standard ML, OCaml, Eiffel, Object Pascal (Delphi), Oberon, Dylan, lenguaje de descripción de hardware VHSIC (VHDL) y varios otros lenguajes.

B y C[editar]

Este estándar uniforme de facto entre la mayoría de los lenguajes de programación finalmente fue cambiado, indirectamente, por un lenguaje compilado minimalista llamado B. Su única aplicación prevista era como vehículo para una primera versión de Unix (entonces muy primitivo), pero también evolucionó hasta convertirse en el muy influyente lenguaje C.

B comenzó como una variante sintácticamente modificada del lenguaje de programación de sistemas BCPL, una versión simplificada (y sin tipo) del lenguaje CPL. En lo que se ha descrito como un proceso de "desmontaje", los operadores and y or de BCPL[6]​ fueron reemplazados por & y | (que luego se convertirían en && y ||, respectivamente).[7]​ En el mismo proceso, el estilo ALGOL := de BCPL fue reemplazado por = en B. Se desconoce el motivo de todo esto.[8]​ Como las actualizaciones de variables no tenían una sintaxis especial en B (como let o similar) y estaban permitidas en las expresiones, este significado no estándar del signo igual significó que la semántica tradicional del signo igual ahora tenía que asociarse con otro símbolo. Ken Thompson utilizó la combinación == ad hoc para esto.

A medida que más tarde se introdujo un pequeño sistema de tipos, B se convirtió en C. La popularidad de este lenguaje junto con su asociación con Unix, llevó a que Java, C# y muchos otros lenguajes siguieran sintácticamente su ejemplo, a pesar de este conflicto innecesario con el significado matemático del signo igual.

Las asignaciones en C tienen un valor, y dado que cualquier valor escalar distinto de cero se interpreta como "verdadero" en una sentencia condicional,[9]​ el código if (x= y) es legal, pero tiene un valor muy diferente del significado de if (x== y). El fragmento de código anterior significa "asignar y a x, y si el nuevo valor de x no es cero, ejecutar la siguiente instrucción". El último fragmento significa "si y solo si x es igual a y, ejecutar la siguiente declaración".[10]

  int x= 1;
  int y= 2;
  if (x= y) {
      /* Este código siempre se ejecutará si y es cualquier cosa menos 0*/
      printf("x is %d and y is %d\n", x, y);
  }

Aunque Java y C# tienen los mismos operadores que C, esta ambigüedad generalmente causa un error de compilación en estos lenguajes, porque la condición if debe ser del tipo boolean y no hay una forma implícita de convertir de otros tipos (por ejemplo, números) en boolean. Entonces, a menos que la variable asignada tenga el tipo boolean (o el tipo contenedor Boolean), se producirá un error de compilación.

En lenguajes tipo ALGOL como Pascal, Delphi y Ada (en el sentido de que permiten la definición de funciones anidadas), y en Python y en muchos lenguajes funcionales entre otros, los operadores de asignación no pueden aparecer en una expresión (incluidas las cláusulas if), lo que impide esta clase de error. Algunos compiladores, como GNU Compiler Collection (GCC), proporcionan una advertencia al compilar código que contiene un operador de asignación dentro de una declaración if, aunque existen algunos usos legítimos de una asignación dentro de una condición if. En tales casos, la asignación debe incluirse explícitamente entre un par de paréntesis adicionales para evitar la advertencia.

De manera similar, algunos lenguajes, como BASIC, usan solo el símbolo = tanto para la asignación como para la igualdad, ya que están sintácticamente separados (como con Pascal, Ada, Python, etc., los operadores de asignación no pueden aparecer en las expresiones).

Algunos programadores adquieren el hábito de escribir comparaciones con una constante en el orden inverso al habitual:

  if (2== a) {/* Mistaken use of= versus== would be a compile-time error */
  }

Si = se usa accidentalmente, el código resultante no es válido porque 2 no es una variable. El compilador generará un mensaje de error, en el que se podrá sustituir el operador adecuado. Este estilo de codificación se denomina comparación a la izquierda o condiciones Yoda.

Esta tabla enumera los diferentes mecanismos para comprobar estos dos tipos de igualdad en varios idiomas:

Lenguaje Igualdad física Igualdad estructural Notas
ALGOL 68 a :=: b or a is b a= b donde a and b son punteros
C, C++ a== b *a== *b donde a and b son punteros
C# object.ReferenceEquals(a, b) a.Equals(b) El operador == por defecto es ReferenceEquals, pero puede quedar sobrecargado para realizar en cambio Equals.
Common Lisp (eq a b) (equal a b)
Erlang a=:= b a== b when a y b son numbers
Go a== b reflect.DeepEqual(*a, *b) donde a y b son punteros
Java a== b a.equals(b)
JavaScript a=== b a== b donde a y b son dos objetos de cadena que contienen caracteres equivalentes, el operador === seguirá devolviendo verdadero.
OCaml, Smalltalk a== b a= b
Pascal a^= b^ a= b
Perl $a== $b $$a== $$b donde $a y $b son referencias a escalares
PHP $a=== $b $a== $b donde $a y $b son objetos
Python a is b a== b
Ruby a.equal?(b) a== b
Scheme (eq? a b) (equal? a b)
Swift a=== b a== b donde a y b tienen tipos de clase
Visual Basic .NET[inequality 1] a Is b o object.ReferenceEquals(a, b) a= b o a.Equals(b) Lo mismo que C#
Objective-C (Cocoa, GNUstep) a== b [a isEqual:b] donde a y b son punteros a objetos que son instancias de NSObject
  1. Patent application: On May 14, 2003, Patente USPTO n.º 20040230959 "IS NOT OPERATOR" fue presentado para el operador ISNOT por empleados de Microsoft. Esta patente fue concedida el 18 de noviembre de 2004.

Ruby usa a=== b para significar "b es miembro del conjunto a", aunque los detalles de lo que significa ser miembro varían considerablemente dependiendo de los tipos de datos involucrados. === se conoce aquí como operador de "igualdad de casos" o "subsunción de casos".

Véase también[editar]

Referencias[editar]

  1. Standard for Posit Arithmetic (2022)
  2. Contributors. «Comparing Objects». PHP Manual. PHP Group. Consultado el 29 de junio de 2014. 
  3. «PHP: Comparison Operators - Manual». Consultado el 31 de julio de 2008. 
  4. Relational and Logical Operators de Mathematica
  5. Alexandrescu, Andrei (2010). The D Programming Language. Addison Wesley. p. 58. ISBN 978-0-321-63536-5. 
  6. Usados no solo en lenguajes parecidos a ALGOL, sino también en FORTRAN y BASIC
  7. Como algunos programadores estaban confundidos por los significados duales (operador bit a bit y conector lógico) de estos nuevos símbolos (según Dennis Ritchie), sólo se mantuvo el significado bit a bit de & y de |.
  8. Aunque Dennis Ritchie ha sugerido que esto puede haber tenido que ver con la "economía de mecanografía", ya que las actualizaciones de variables pueden ser más frecuentes que las comparaciones en ciertos tipos de programas.
  9. Un valor escalar cero se interpreta como falso, mientras que cualquier valor escalar distinto de cero se interpreta como verdadero; esto se usa típicamente con tipos de números enteros, similar a las convenciones del lenguaje ensamblador.
  10. Brian Kernighan and Dennis Ritchie (1988) [1978]. The C Programming Language (Second edición). Prentice Hall. , 19