En informática , una comparación de tres vías toma dos valores A y B pertenecientes a un tipo con un orden total y determina si A < B, A = B o A > B en una sola operación, de acuerdo con la ley matemática de tricotomía .
Puede implementarse en términos de una función (como strcmp
en C ), un método (como compareTo
en Java ) o un operador (como el operador de la nave espacial <=>
en Perl ).
Muchos procesadores tienen conjuntos de instrucciones que admiten dicha operación en tipos primitivos. Algunas máquinas tienen enteros con signo basados en una representación de signo y magnitud o en complemento de uno (ver representaciones de números con signo ), las cuales permiten un cero positivo y negativo diferenciados . Esto no viola la tricotomía siempre que se adopte un orden total consistente: −0 = +0 o −0 < +0 es válido. Los tipos de punto flotante comunes , sin embargo, tienen una excepción a la tricotomía: hay un valor especial "NaN" ( No es un número ) tal que x < NaN, x > NaN y x = NaN son todos falsos para todos los valores de punto flotante x (incluido el propio NaN).
En C , las funciones strcmp
y memcmp
realizan una comparación de tres vías entre cadenas y búferes de memoria, respectivamente. Devuelven un número negativo cuando el primer argumento es lexicográficamente menor que el segundo, cero cuando los argumentos son iguales y un número positivo en caso contrario. Esta convención de devolver el "signo de la diferencia" se extiende a funciones de comparación arbitrarias mediante la función de clasificación estándar qsort
, que toma una función de comparación como argumento y le exige que la cumpla.
En Perl (solo para comparaciones numéricas, el cmp
operador se usa para comparaciones léxicas de cadenas), PHP (desde la versión 7), Ruby y Apache Groovy , el "operador de nave espacial" <=>
devuelve los valores −1, 0 o 1 dependiendo de si A < B, A = B o A > B, respectivamente. Las funciones Python 2.x cmp
(eliminadas en 3.x), OCaml compare
y Kotlin compareTo
calculan lo mismo. En la biblioteca estándar de Haskell , la función de comparación de tres vías está definida para todos los tipos de la clase ; devuelve tipo , cuyos valores son (menor que), (igual) y (mayor que): [1]compare
Ord
Ordering
LT
EQ
GT
datos Pedido = LT | Ecualizador | GT
Muchos lenguajes de programación orientados a objetos tienen un método de comparación de tres vías , que realiza una comparación de tres vías entre el objeto y otro objeto determinado. Por ejemplo, en Java , cualquier clase que implemente la Comparable
interfaz tiene un método compareTo que devuelve un entero negativo, cero o un entero positivo, o arroja un NullPointerException
(si uno o ambos objetos son null
). De manera similar, en el marco .NET , cualquier clase que implemente la IComparable
interfaz tiene dicho método CompareTo.
Desde la versión 1.5 de Java, lo mismo se puede calcular utilizando el Math.signum
método estático si la diferencia se puede conocer sin problemas computacionales como el desbordamiento aritmético que se menciona a continuación. Muchos lenguajes informáticos permiten la definición de funciones, por lo que una comparación (A, B) podría diseñarse apropiadamente, pero la pregunta es si su definición interna puede emplear algún tipo de sintaxis de tres vías o si debe recurrir a pruebas repetidas.
Al implementar una comparación de tres vías donde aún no está disponible un operador o método de comparación de tres vías, es común combinar dos comparaciones, como A = B y A < B, o A < B y A > B. En principio , un compilador podría deducir que estas dos expresiones podrían sustituirse por una sola comparación seguida de múltiples pruebas del resultado, pero no se menciona esta optimización en los textos sobre el tema.
En algunos casos, se puede simular una comparación triple restando A y B y examinando el signo del resultado, aprovechando instrucciones especiales para examinar el signo de un número. Sin embargo, esto requiere que el tipo de A y B tenga una diferencia bien definida. Los enteros con signo de ancho fijo pueden desbordarse cuando se restan, los números de punto flotante tienen el valor NaN con signo indefinido y las cadenas de caracteres no tienen ninguna función de diferencia correspondiente a su orden total. A nivel de máquina, generalmente se rastrea el desbordamiento y se puede usar para determinar el orden después de la resta, pero esta información generalmente no está disponible para lenguajes de nivel superior.
En un caso de un condicional de tres vías proporcionado por el lenguaje de programación, la ahora obsoleta declaración IF aritmética de tres vías de Fortran considera el signo de una expresión aritmética y ofrece tres etiquetas para saltar de acuerdo con el signo del resultado:
SI ( expresión ) negativo , cero , positivo
La función de biblioteca común strcmp en C y lenguajes relacionados es una comparación lexicográfica de tres vías de cadenas; sin embargo, estos lenguajes carecen de una comparación general de tres vías de otros tipos de datos.
El operador de comparación de tres vías u "operador de nave espacial" para números se denomina como <=>
en Perl , Ruby , Apache Groovy , PHP , Eclipse Ceilán y C++ , y se denomina operador de nave espacial . [2]
En C++ , la revisión de C++ 20 agrega el operador de nave espacial <=>
, que de manera similar devuelve el signo de la diferencia y también puede devolver diferentes tipos (convertibles a enteros con signo) dependiendo del rigor de la comparación. [3]
El origen del nombre se debe a que a Randal L. Schwartz le recuerda a la nave espacial de un juego HP BASIC Star Trek . [4] Otro programador ha sugerido que se llamó así porque se parecía al caza TIE de Darth Vader en la saga Star Wars . [5]
Ejemplo en PHP:
eco 1 <=> 1 ; // 0 eco 1 <=> 2 ; // -1 eco 2 <=> 1 ; // 1
Las comparaciones tripartitas tienen la propiedad de ser fáciles de componer y construir comparaciones lexicográficas de tipos de datos no primitivos, a diferencia de las comparaciones bidireccionales.
A continuación se muestra un ejemplo de composición en Perl.
sub comparar ($$) { mi ( $a , $b ) = @_ ; return $a -> { unidad } cmp $b -> { unidad } || $a -> { rango } <=> $b -> { rango } || $a -> { nombre } cmp $b -> { nombre }; }
Tenga en cuenta que cmp
, en Perl, es para cadenas, ya que <=>
es para números. Los equivalentes bidireccionales tienden a ser menos compactos pero no necesariamente menos legibles. Lo anterior aprovecha la evaluación de cortocircuito del ||
operador y el hecho de que 0 se considera falso en Perl. Como resultado, si la primera comparación es igual (por lo tanto, se evalúa como 0), "caerá" en la segunda comparación, y así sucesivamente, hasta que encuentre una que sea distinta de cero, o hasta que llegue al final.
En algunos lenguajes, incluidos Python , Ruby , Haskell , etc., la comparación de listas se realiza lexicográficamente, lo que significa que es posible construir una cadena de comparaciones como el ejemplo anterior colocando los valores en listas en el orden deseado; por ejemplo, en Rubí:
[ a . unidad , un . rango , un . nombre ] <=> [ b . unidad , b . rango , b . nombre ]
<=>
sintaxis, en un artículo titulado "Comparación consistente". Consulte "Comparación coherente". Se fusionó con éxito en el borrador de C++ 20 en noviembre de 2017.