O que é o operador Ruby <=> (nave espacial)?

262

O que é o <=>operador Ruby (nave espacial)? O operador é implementado por outros idiomas?

Justin Ethier
fonte
1
Agora, que tal comparar matrizes? Ele diz no livro "compara elemento por elemento, retorna 0 se iguais, -1 se menor, 1 se for maior, mas o que dizer [1,3,2] <=> [2,2,2]?
SF.
3
@SF, quando as pessoas comparam arrays, geralmente pretendem comparar lexicograficamente (como em um dicionário, ou seja, [1,3,2] <[2,2,2] porque os primeiros elementos são diferentes). Raramente (fe no Matlab), a comparação de arrays retorna uma matriz de resultados por elemento; neste caso: [-1, 1, 0].
Liori 16/04
Observe que as matrizes que contêm elementos nulos são comparáveis ​​se os elementos anteriores a nulo forem diferentes e não comparáveis ​​se um nulo precisar ser comparado com um nulo. Ou seja, [1, zero] <=> [2, 3] => -1, mas [1, zero] <=> [1, 3] => zero. Isso é péssimo, basicamente.
precisa
Ao comparar matrizes como [1,nil] <=> [1,3]você obtém um nilpor causa da consistência do algoritmo, compare cada elemento sucessivamente até que o <=>resultado NÃO seja 0. Não há como o Ruby declarar menor ou maior do que neste exemplo, pois uma comparação simplesmente não pode ser feita. O nildeve ser tratado como "não igual". Se você conhece alguma coisa sobre os dados e, por exemplo, deseja tratar nilcomo 0, Ruby facilita isso.
lilole

Respostas:

359

Perl foi provavelmente o primeiro idioma a usá-lo. Groovy é outro idioma que o suporta. Basicamente em vez de voltar 1( true) ou 0( false), dependendo se os argumentos forem iguais ou diferentes, o operador nave espacial irá retornar 1, 0ou −1, dependendo do valor do argumento relativo esquerdo para o argumento direita.

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

É útil para classificar uma matriz.

TonyArra
fonte
27
Exatamente. Eu penso nisso como uma versão muito elegante do Comparable do Java.
Mike Reedell #
12
analógico em c # é IComparable.CompareTo
Sergey Mirvoda
1
Na verdade, acho que qualquer valor negativo ou positivo pode ser retornado. 0 ainda significa igualdade.
superluminary
1
@ superluminário Diferentemente da função strcmp de C, x <=> y ​​é projetado especificamente para retornar -1, 0, 1 ou zero se xey não forem comparáveis ​​(no Ruby e em outros idiomas que o utilizam AFAIK). Isso facilita a sobrecarga do operador, como no mixin Comparable do Ruby. No Perl, onde o operador provavelmente se originou, ele foi usado principalmente para simplificar a sintaxe "classificar lista de bloqueios". O BLOCO é uma sub-rotina que pode retornar qualquer número positivo, número negativo ou 0, dependendo de como os itens da lista devem ser classificados. O operador da nave espacial é conveniente para usar no bloco.
precisa saber é o seguinte
2
Note-se que se os dois objetos comparados não são comparáveis, você recebe um nulo
Gamov
70

O método da nave espacial é útil quando você o define em sua própria classe e inclui o módulo Comparável . Sua classe obtém os >, < , >=, <=, ==, and between?métodos gratuitamente.

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
fonte
20

É um operador de comparação geral. Ele retorna -1, 0 ou +1, dependendo de seu receptor ser menor que, igual a ou maior que seu argumento.

gnovice
fonte
18

Vou explicar com um exemplo simples

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

    Ruby começará a comparar cada elemento de ambas as matrizes do lado esquerdo. 1para matriz esquerda é menor que a 2matriz direita. Portanto, a matriz esquerda é menor que a matriz direita. A saída será -1.

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

    Como acima, ele primeiro comparará o primeiro elemento que é igual e, em seguida, o segundo elemento; nesse caso, o segundo elemento da matriz esquerda é maior, portanto a saída é 1.

Anil Maurya
fonte
apenas compara o primeiro elemento esquerdo de cada matriz ou continua a comparar outros elementos também? boa explicação
pontapé Buttowski
1
@KickButtowski continua a comparar outros elementos, a menos que encontre um número desigual.
Anil Maurya
5

Como esse operador reduz as comparações com uma expressão inteira, ele fornece a maneira mais geral de classificar crescente ou decrescente com base em várias colunas / atributos.

Por exemplo, se eu tenho uma matriz de objetos, posso fazer coisas assim:

# `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

Esse padrão básico pode ser generalizado para classificar por qualquer número de colunas, em qualquer permutação de ascensão / descida em cada uma.

lilole
fonte
Bons exemplos, apenas que o último não funciona conforme o esperado. Os fatores devem ser potências de dois em ordem decrescente, ou seja, 8, -4, 2, 1. A maneira como você o escreveu (com fatores 4, -3,2,1), por exemplo, "idade + sobrenome" conta mais que "zip "...
Elmar Zander
Eu não acho que esses números significam o que você pensa que eles significam. Cada fator multiplica o signum, que será -1, 0 ou 1. Potências de 2 não importam aqui. O -3 * (idade == b.age) é exatamente igual a 3 * (idade == b.age). O sinal do resultado é o que o torna asc ou desc.
lilole
Não, isso importa muito. O fator para zip deve ser maior que a soma (absoluta) de todos os outros fatores, e o fator para idade deve ser maior que a soma (absoluta) dos fatores de último e primeiro, e assim por diante. E a menor sequência de números que cumpre essa é a sequência de potências de dois ... E BTW, se você ler o meu comentário com cuidado, você teria visto que eu incluído no sinal de menos ...
Elmar Zander
1
Ok, talvez eu deva elaborar um pouco mais sobre isso: com os fatores (4, -3,2,1) e os resultados da espaçonave op (1,1, -1, -1) a soma ponderada é -2, mas precisa ser positivo! Caso contrário, o zip maior virá antes do zip menor. Isso não acontecerá com fatores (8, -4,2,1).
Elmar Zander
1
Ah agora, se a classificação em> 2 colunas for necessária, a potência de 2. Obrigado por ajudar a corrigir isso. Desculpe, mundo, se a classificação de três ou mais colunas resultou errada.
lilole
-2

O que é <=> (o operador 'Nave espacial')

De acordo com a RFC que introduziu a operadora , $ 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

Exemplo:

//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

MAIS:

// 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
fonte