¿Qué es el operador Ruby <=> (nave espacial)?

263

¿Qué es el <=>operador Ruby (nave espacial)? ¿El operador está implementado por otros idiomas?

Justin Ethier
fuente
1
¿Y qué hay de comparar matrices? Decía en el libro "compara elemento por elemento, devuelve 0 si es igual, -1 si es menor, 1 si es mayor, pero ¿qué pasa [1,3,2] <=> [2,2,2]?
SF.
3
@SF, cuando las personas comparan matrices, generalmente quieren comparar lexicográficamente (como en un diccionario, es decir, [1,3,2] <[2,2,2] porque los primeros elementos son diferentes). Raramente (fe en Matlab) la comparación de matrices devuelve una matriz de resultados por elemento; en este caso: [-1, 1, 0].
liori
Tenga en cuenta que las matrices que contienen elementos nulos son comparables si los elementos anteriores a cualquier nulo son diferentes, y no comparables si un nulo debe compararse con un nulo. Es decir, [1, nulo] <=> [2, 3] => -1, pero [1, nulo] <=> [1, 3] => nulo. Esto apesta, básicamente.
cliffordheath
Al comparar matrices como las [1,nil] <=> [1,3]que obtiene, nildebido a la coherencia del algoritmo, compare cada elemento por turnos hasta que el <=>resultado NO sea 0. No hay forma de que Ruby declare menor o mayor que en este ejemplo, ya que simplemente no se puede hacer una comparación. El nildebe ser tratado como "no igual". Si usted sabe algo acerca de los datos, por ejemplo, y quiere tratar nilcomo 0, Ruby hace tan fácil.
lilole

Respuestas:

360

Perl fue probablemente el primer idioma en usarlo. Groovy es otro idioma que lo admite. Básicamente en lugar de regresar 1( true) o 0( false) dependiendo de si los argumentos son iguales o desiguales, el operador de la nave espacial regresará 1, 0o −1dependiendo del valor del argumento de la izquierda en relación con el argumento de la derecha.

a <=> b :=
  if a < b then return -1
  if a = b then return  0
  if a > b then return  1
  if a and b are not comparable then return nil

Es útil para ordenar una matriz.

TonyArra
fuente
27
Exactamente. Lo considero una versión muy elegante de Comparable de Java.
Mike Reedell
12
analog in c # is IComparable.CompareTo
Sergey Mirvoda el
1
En realidad, creo que cualquier valor negativo o positivo puede ser devuelto. 0 todavía significa igualdad.
superluminary
1
@superluminary A diferencia de la función strcmp de C, x <=> y ​​está diseñado específicamente para devolver -1, 0, 1 o cero si x e y no son comparables (en Ruby y en cualquier otro lenguaje que lo use AFAIK). Esto facilita la sobrecarga del operador, como por ejemplo para el mixin comparable de Ruby. En Perl, donde el operador probablemente se originó, se usó principalmente para simplificar la sintaxis "ordenar LISTA DE BLOQUES". El BLOQUE es una subrutina que puede devolver cualquier número positivo, negativo o 0 dependiendo de cómo se deben ordenar los elementos de la lista. El operador de nave espacial es conveniente de usar en el bloque.
TonyArra
2
Tenga en cuenta que si los dos objetos comparados no son comparables, obtendrá un valor nulo
gamov
70

El método de nave espacial es útil cuando lo define en su propia clase e incluye el módulo Comparable . Tu clase obtiene los >, < , >=, <=, ==, and between?métodos gratis.

class Card
  include Comparable
  attr_reader :value

  def initialize(value)
    @value = value
  end

  def <=> (other) #1 if self>other; 0 if self==other; -1 if self<other
    self.value <=> other.value
  end

end

a = Card.new(7)
b = Card.new(10)
c = Card.new(8)

puts a > b # false
puts c.between?(a,b) # true

# Array#sort uses <=> :
p [a,b,c].sort # [#<Card:0x0000000242d298 @value=7>, #<Card:0x0000000242d248 @value=8>, #<Card:0x0000000242d270 @value=10>]
steenslag
fuente
20

Es un operador de comparación general. Devuelve un -1, 0 o +1 dependiendo de si su receptor es menor, igual o mayor que su argumento.

gnovice
fuente
18

Explicaré con un simple ejemplo

  1. [1,3,2] <=> [2,2,2]

    Ruby comenzará a comparar cada elemento de ambos conjuntos desde el lado izquierdo. 1para la matriz izquierda es más pequeña que 2la matriz derecha. Por lo tanto, la matriz izquierda es más pequeña que la matriz derecha. La salida será -1.

  2. [2,3,2] <=> [2,2,2]

    Como arriba, primero comparará el primer elemento que es igual, luego comparará el segundo elemento, en este caso, el segundo elemento de la matriz izquierda es mayor, por lo tanto, la salida es 1.

Anil Maurya
fuente
¿solo compara el primer elemento izquierdo de cada matriz o continúa comparando también otros elementos? buena explicación
Kick Buttowski
1
@KickButtowski continúa comparando otros elementos a menos que encuentre un número desigual.
Anil Maurya
5

Dado que este operador reduce las comparaciones a una expresión entera, proporciona la forma de propósito más general para ordenar ascendente o descendente en función de múltiples columnas / atributos.

Por ejemplo, si tengo una variedad de objetos, puedo hacer cosas como esta:

# `sort!` modifies array in place, avoids duplicating if it's large...

# Sort by zip code, ascending
my_objects.sort! { |a, b| a.zip <=> b.zip }

# Sort by zip code, descending
my_objects.sort! { |a, b| b.zip <=> a.zip }
# ...same as...
my_objects.sort! { |a, b| -1 * (a.zip <=> b.zip) }

# Sort by last name, then first
my_objects.sort! { |a, b| 2 * (a.last <=> b.last) + (a.first <=> b.first) }

# Sort by zip, then age descending, then last name, then first
# [Notice powers of 2 make it work for > 2 columns.]
my_objects.sort! do |a, b|
      8 * (a.zip   <=> b.zip) +
     -4 * (a.age   <=> b.age) +
      2 * (a.last  <=> b.last) +
          (a.first <=> b.first)
end

Este patrón básico se puede generalizar para ordenar por cualquier número de columnas, en cualquier permutación de ascendente / descendente en cada una.

lilole
fuente
Buenos ejemplos, solo que el último no funciona como se esperaba. Los factores deben ser potencias de dos en orden descendente, es decir, 8, -4, 2, 1. La forma en que lo escribió (con factores 4, -3,2,1), por ejemplo, "edad + apellido" cuenta más que "zip "...
Elmar Zander
No creo que esos números signifiquen lo que crees que significan. Cada factor multiplica el signum, que será -1, 0 o 1. Las potencias de 2 no importan aquí. El -3 * (a.age <=> b.age) es exactamente el mismo que 3 * (b.age <=> a.age). El signo del resultado es lo que lo hace asc o desc.
lilole
No, importa mucho. El factor para zip debe ser mayor que la suma (absoluta) de todos los demás factores, y el factor para la edad debe ser mayor que la suma (absoluta) de los factores de último y primero, y así sucesivamente. Y la secuencia más pequeña de números que cumple esa es la secuencia de poderes de dos ... Y, por cierto, si lees mi comentario cuidadosamente, habrías visto que incluí el signo menos ...
Elmar Zander
1
Ok, tal vez explique un poco más sobre eso: con los factores (4, -3,2,1) y los resultados de la nave espacial op (1,1, -1, -1) la suma ponderada es -2, pero tiene que ser positivo! De lo contrario, la cremallera más grande vendrá antes que la cremallera más pequeña. Esto no sucederá con los factores (8, -4,2,1).
Elmar Zander
1
Ah, ya veo, si se ordena en> 2 columnas, entonces se requieren los poderes de 2. Gracias por ayudar a corregir esto. Lo siento, mundo, si tu clasificación de 3 o más columnas resultó incorrecta.
lilole
-2

Qué es <=> (El operador de la 'nave espacial')

Según el RFC que introdujo el operador , $ a <=>$ b

 -  0 if $a == $b
 - -1 if $a < $b
 -  1 if $a > $b

 - Return 0 if values on either side are equal
 - Return 1 if value on the left is greater
 - Return -1 if the value on the right is greater

Ejemplo:

//Comparing Integers

echo 1 <=> 1; //ouputs 0
echo 3 <=> 4; //outputs -1
echo 4 <=> 3; //outputs 1

//String Comparison

echo "x" <=> "x"; // 0
echo "x" <=> "y"; //-1
echo "y" <=> "x"; //1

MÁS:

// Integers
echo 1 <=> 1; // 0
echo 1 <=> 2; // -1
echo 2 <=> 1; // 1

// Floats
echo 1.5 <=> 1.5; // 0
echo 1.5 <=> 2.5; // -1
echo 2.5 <=> 1.5; // 1

// Strings
echo "a" <=> "a"; // 0
echo "a" <=> "b"; // -1
echo "b" <=> "a"; // 1

echo "a" <=> "aa"; // -1
echo "zz" <=> "aa"; // 1

// Arrays
echo [] <=> []; // 0
echo [1, 2, 3] <=> [1, 2, 3]; // 0
echo [1, 2, 3] <=> []; // 1
echo [1, 2, 3] <=> [1, 2, 1]; // 1
echo [1, 2, 3] <=> [1, 2, 4]; // -1

// Objects
$a = (object) ["a" => "b"]; 
$b = (object) ["a" => "b"]; 
echo $a <=> $b; // 0
RïshïKêsh Kümar
fuente