Cos'è l'operatore Ruby <=> (astronave)?


262

Cos'è l' <=>operatore Ruby (astronave)? L'operatore è implementato in altre lingue?


1
E che dire del confronto tra array? Disse nel libro "confronta elemento per elemento, restituisce 0 se uguale, -1 se minore, 1 se maggiore, ma che dire [1,3,2] <=> [2,2,2]?
SF.

3
@SF, quando le persone confrontano gli array, di solito significano confrontare lessicograficamente (come in un dizionario, cioè [1,3,2] <[2,2,2] perché i primi elementi sono diversi). Raramente (fe in Matlab) il confronto tra array restituisce una matrice di risultati per elemento; in questo caso: [-1, 1, 0].
liori,

Si noti che le matrici che contengono elementi nulli sono comparabili se gli elementi prima di uno zero sono diversi e non confrontabili se uno zero deve essere confrontato con un valore diverso da zero. Vale a dire [1, zero] <=> [2, 3] => -1, ma [1, zero] <=> [1, 3] => zero. Questo fa schifo, fondamentalmente.
cliffordheath

Quando si confrontano le matrici come se [1,nil] <=> [1,3]si ottenga una a nilcausa della coerenza dell'algoritmo, confrontando ogni elemento a turno fino a quando il <=>risultato NON è 0. Non c'è modo per Ruby di dichiarare minore o maggiore di in questo esempio, dal momento che un confronto semplicemente non può essere fatto. L' nildovrebbero essere trattati come "diverso". Se sai qualcosa sui dati, e ad esempio vuoi trattare nilcome 0, Ruby lo rende facile.
lilole,

Risposte:


359

Perl era probabilmente la prima lingua ad usarlo. Groovy è un'altra lingua che lo supporta. Fondamentalmente invece di restituire 1( true) o 0( false) a seconda che gli argomenti sono uguali o disuguali, l'operatore spaziale tornerà 1, 0oppure −1a seconda del valore dell'argomento sinistra rispetto all'argomento a destra.

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

È utile per ordinare un array.


27
Esattamente. La considero una versione molto elegante di Java Comparable.
Mike Reedell,

12
analogo in c # è IComparable.CompareTo
Sergey Mirvoda

1
In realtà penso che qualsiasi valore negativo o positivo possa essere restituito. 0 significa ancora uguaglianza.
superluminario,

1
@superluminary A differenza della funzione strcmp di C, x <=> y ​​è progettato specificamente per restituire solo -1, 0, 1 o zero se xey non sono comparabili (in Ruby e in qualsiasi altra lingua che lo utilizza AFAIK). Ciò semplifica il sovraccarico dell'operatore, ad esempio per il mixin comparabile di Ruby. In Perl, dove l'operatore molto probabilmente ha avuto origine, è stato utilizzato principalmente per semplificare la sintassi "ordina BLOCCO ELENCO". BLOCK è una subroutine che può restituire qualsiasi numero positivo, numero negativo o 0 a seconda di come devono essere ordinati gli elementi dell'elenco. L'operatore di astronave è comodo da usare nel blocco.
TonyArra,

2
Si noti che se i due oggetti confrontati non sono comparabili, si ottiene uno zero
gamov

70

Il metodo dell'astronave è utile quando lo definisci nella tua classe e includi il modulo Comparable . La tua classe quindi ottiene i >, < , >=, <=, ==, and between?metodi 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>]

20

È un operatore di confronto generale. Restituisce un -1, 0 o +1 a seconda che il suo destinatario sia minore, uguale o maggiore del suo argomento.


18

Spiegherò con un semplice esempio

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

    Ruby inizierà a confrontare ciascun elemento di entrambi gli array dal lato sinistro. 1per l'array sinistro è più piccolo 2dell'array destro. Quindi l'array sinistro è più piccolo dell'array destro. L'output sarà -1.

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

    Come sopra confronterà prima il primo elemento che è uguale, quindi confronterà il secondo elemento, in questo caso il secondo elemento dell'array sinistro è maggiore quindi l'output lo è 1.


confronta solo il primo elemento sinistro di ciascun array o continua a confrontare anche altri elementi? buona spiegazione
Calcia Buttowski il

1
@KickButtowski continua a confrontare altri elementi a meno che non trovi un numero ineguale.
Anil Maurya,

5

Poiché questo operatore riduce i confronti a un'espressione intera, fornisce il modo più generico di ordinare in ordine crescente o decrescente in base a più colonne / attributi.

Ad esempio, se ho una serie di oggetti posso fare cose come questa:

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

Questo modello di base può essere generalizzato per ordinare in base a qualsiasi numero di colonne, in qualsiasi permutazione di crescente / decrescente su ciascuna.


Bei esempi, solo che l'ultimo non funziona come previsto. I fattori dovrebbero essere poteri di due in ordine decrescente, ovvero 8, -4, 2, 1. Il modo in cui lo hai scritto (con i fattori 4, -3,2,1), ad esempio "età + cognome" conta più di "zip "...
Elmar Zander il

Non penso che quei numeri significino cosa pensi che significhino. Ogni fattore moltiplica il signum, che sarà -1, 0 o 1. I poteri di 2 non contano qui. -3 * (a.age <=> b.age) è esattamente uguale a 3 * (b.age <=> a.age). Il segno del risultato è ciò che lo rende asc o desc.
lilole,

No, importa molto. Il fattore zip deve essere maggiore della somma (assoluta) di tutti gli altri fattori e il fattore età deve essere maggiore della somma (assoluta) dei fattori di ultimo e primo, e così via. E la più piccola sequenza di numeri che soddisfa quella è la sequenza di poteri di due ... E BTW se leggi attentamente il mio commento, vedresti che ho incluso il segno meno ...
Elmar Zander

1
Ok, forse ne parlerò un po 'di più: con i fattori (4, -3,2,1) e i risultati dell'astronave op (1,1, -1, -1) la somma ponderata è -2, ma deve essere positivo! Altrimenti la zip più grande verrà prima della zip più piccola. Questo non accadrà con i fattori (8, -4,2,1).
Elmar Zander,

1
Ah, vedo ora, se l'ordinamento su> 2 colonne è richiesto il potere di 2. Grazie per l'aiuto per correggere questo. Mi dispiace mondo se l'ordinamento di 3 o più colonne si è rivelato errato.
lilole,

-2

Che cos'è <=> (l'operatore "Nave spaziale")

Secondo la RFC che ha introdotto l'operatore , $ 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

Esempio:

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

DI PIÙ:

// 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.