Co to jest operator Ruby <=> (statek kosmiczny)?

262

Co to jest <=>operator Ruby (statek kosmiczny)? Czy operator jest implementowany w innych językach?

Justin Ethier
źródło
1
A co z porównywaniem tablic? W książce napisano: „porównuje element po elemencie, zwraca 0, jeśli jest równy, -1, jeśli mniejszy, 1, jeśli większy, ale co z tego [1,3,2] <=> [2,2,2]?
SF.
3
@SF, kiedy ludzie porównują tablice, zwykle mają na celu porównanie leksykograficzne (jak w słowniku, tj. [1,3,2] <[2,2,2], ponieważ pierwsze elementy są różne). Rzadko (np. W Matlabie) porównanie tablic zwraca tablicę wyników na element; w tym przypadku: [-1, 1, 0].
liori
Zauważ, że tablice zawierające elementy zerowe są porównywalne, jeśli elementy poprzedzające zero są różne i nie są porównywalne, jeśli zero należy porównać z wartością zerową. Tj. [1, zero] <=> [2, 3] => -1, ale [1, zero] <=> [1, 3] => zero. Zasadniczo to jest do bani.
cliffordheath
Porównując tablice w taki sposób, [1,nil] <=> [1,3]że otrzymujesz wynik nilze względu na spójność algorytmu, porównując kolejno każdy element, aż <=>wynik NIE będzie 0. Ruby nie ma możliwości zadeklarowania wartości mniejszej lub większej niż w tym przykładzie, ponieważ po prostu nie można dokonać porównania. nilPowinny być traktowane jako „nie równa się”. Jeśli wiesz coś o danych i np. Chcesz traktować niljako 0, Ruby to ułatwia.
lilole,

Odpowiedzi:

359

Perl był prawdopodobnie pierwszym językiem, który go używał. Groovy to kolejny język, który go obsługuje. Zasadniczo zamiast powrocie 1( true) lub 0( false), w zależności od tego, czy argumenty są równe lub nierówne, operator statku kosmicznego powróci 1, 0lub −1w zależności od wartości lewego argumentu w stosunku do prawego argumentu.

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

Jest to przydatne do sortowania tablicy.

TonyArra
źródło
27
Dokładnie. Uważam to za bardzo elegancką wersję porównywalnego oprogramowania Java.
Mike Reedell
12
analog w c # jest IComparable.CompareTo
Sergey Mirvoda
1
Właściwie myślę, że można zwrócić dowolną wartość ujemną lub dodatnią. 0 wciąż oznacza równość.
superluminarny,
1
@superluminary W przeciwieństwie do funkcji strcmp C, x <=> y ​​jest zaprojektowany specjalnie do zwracania -1, 0, 1 lub zero, jeśli xiy nie są porównywalne (w Ruby i innych językach, które go używają AFAIK). Ułatwia to przeciążenie operatora, na przykład w przypadku porównywalnej mieszanki Ruby. W Perlu, gdzie najprawdopodobniej pochodzi operator, był używany głównie w celu uproszczenia składni „sort BLOCK LIST”. BLOK jest podprogramem, który może zwrócić dowolną liczbę dodatnią, liczbę ujemną lub 0, w zależności od sposobu sortowania elementów listy. Operator statku kosmicznego jest wygodny w użyciu w bloku.
TonyArra
2
Zauważ, że jeśli dwa porównywane obiekty nie są porównywalne, otrzymasz zero
gamov
70

Metoda statku kosmicznego jest przydatna, gdy definiujesz ją we własnej klasie i obejmuje moduł porównywalny . Twoja klasa otrzymuje >, < , >=, <=, ==, and between?metody za darmo.

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
źródło
20

Jest to ogólny operator porównania. Zwraca wartość -1, 0 lub +1 w zależności od tego, czy jego odbiornik jest mniejszy, równy lub większy niż argument.

gnovice
źródło
18

Wyjaśnię prostym przykładem

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

    Ruby rozpocznie porównywanie każdego elementu obu tablic od lewej strony. 1dla lewej tablicy jest mniejszy niż 2prawej tablicy. Dlatego lewa tablica jest mniejsza niż prawa tablica. Wyjście będzie -1.

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

    Jak wyżej, najpierw porównuje pierwszy element, który jest równy, a następnie porównuje drugi element, w tym przypadku drugi element lewej tablicy jest większy, a zatem wynik jest 1.

Anil Maurya
źródło
czy po prostu porównuje pierwszy lewy element każdej tablicy, czy też kontynuuje także porównanie innych elementów? dobre wyjaśnienie
Kick Buttowski
1
@KickButtowski kontynuuje porównywanie innych elementów, chyba że znajdzie nierówną liczbę.
Anil Maurya
5

Ponieważ ten operator redukuje porównania do wyrażenia liczb całkowitych, zapewnia najbardziej ogólny sposób sortowania rosnąco lub malejąco na podstawie wielu kolumn / atrybutów.

Na przykład, jeśli mam tablicę obiektów, mogę robić takie rzeczy:

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

Ten podstawowy wzorzec można uogólnić, aby posortować według dowolnej liczby kolumn, w dowolnej kombinacji rosnącej / malejącej na każdej z nich.

lilole
źródło
Ładne przykłady, tyle że ostatni nie działa zgodnie z oczekiwaniami. Czynniki powinny być potęgami dwóch w porządku malejącym, tj. 8, -4, 2, 1. Sposób, w jaki to napisałeś (z czynnikami 4, -3,2,1), np. „Wiek + nazwisko” liczy się bardziej niż „zip „...
Elmar Zander,
Nie sądzę, żeby te liczby miały na myśli to, co myślisz. Każdy czynnik zwielokrotnia signum, które wyniesie -1, 0 lub 1. Moce 2 nie mają tutaj znaczenia. Wartość -3 * (a.age <=> b.age) jest dokładnie taka sama jak 3 * (b.age <=> a.age). Znak wyniku jest tym, co czyni go rosnącym lub malejącym.
lilole
Nie, to ma bardzo duże znaczenie. Współczynnik dla zip musi być większy niż (absolutna) suma wszystkich innych czynników, a współczynnik dla wieku musi być większy niż (absolutna) suma czynników ostatniego i pierwszego itd. I najmniejsza sekwencja liczb, która się spełnia, to sekwencja potęg dwóch ... A BTW, jeśli dokładnie przeczytasz mój komentarz, zobaczyłbyś, że zawarłem znak minus ...
Elmar Zander
1
Ok, może rozwinę nieco więcej na ten temat: przy współczynnikach (4, -3,2,1) i wynikach ze statku kosmicznego op (1,1, -1, -1) ważona suma wynosi -2, ale to musi być pozytywne! W przeciwnym razie większy zamek pojawi się przed mniejszym zamkiem. Nie stanie się tak z czynnikami (8, -4,2,1).
Elmar Zander
1
Ach, rozumiem teraz, jeśli sortowanie na> 2 kolumnach, to wymagana jest moc 2. Dziękujemy za pomoc w rozwiązaniu tego problemu. Przepraszam świat, jeśli sortowanie 3 lub więcej kolumn okazało się nieprawidłowe.
lilole
-2

Co to jest <=> (operator „statku kosmicznego”)

Według RFC, która wprowadziła operatora , $ 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

Przykład:

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

WIĘCEJ:

// 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
źródło