Was ist der Ruby <=> (Raumschiff) Operator?

262

Was ist der Rubin? <=> Betreiber (Raumschiff)? Wird der Operator in einer anderen Sprache implementiert?

Justin Ethier
quelle
1
Was ist nun mit dem Vergleichen von Arrays? Es heißt in dem Buch "vergleicht Element für Element, gibt 0 zurück, wenn gleich, -1 wenn kleiner, 1 wenn größer, aber was ist mit [1,3,2] <=> [2,2,2]?
SF.
3
@SF, wenn Leute Arrays vergleichen, meinen sie normalerweise, lexikographisch zu vergleichen (wie in einem Wörterbuch, dh [1,3,2] <[2,2,2], weil die ersten Elemente unterschiedlich sind). In seltenen Fällen (z. B. in Matlab) gibt ein Array-Vergleich ein Array von Ergebnissen pro Element zurück. in diesem Fall: [-1, 1, 0].
Liori
Beachten Sie, dass Arrays, die keine Elemente enthalten, vergleichbar sind, wenn die Elemente vor null unterschiedlich sind, und nicht vergleichbar, wenn eine Null mit einer Nicht-Null verglichen werden muss. Dh [1, null] <=> [2, 3] => -1, aber [1, null] <=> [1, 3] => null. Das ist im Grunde genommen scheiße.
Cliffordheath
Wenn Sie Arrays vergleichen, wie [1,nil] <=> [1,3]Sie sie nilaufgrund der Konsistenz des Algorithmus erhalten, vergleichen Sie jedes Element nacheinander, bis das <=>Ergebnis NICHT ist 0. Ruby kann in diesem Beispiel nicht kleiner oder größer als deklarieren, da ein Vergleich einfach nicht möglich ist. Das nilsollte als "ungleich" behandelt werden. Wenn Sie etwas über die Daten wissen und z. B. nilals behandeln möchten 0, macht Ruby dies einfach.
Lilole

Antworten:

359

Perl war wahrscheinlich die erste Sprache, die es verwendete. Groovy ist eine andere Sprache, die es unterstützt. Anstatt 1( true) oder 0( false) zurückzugeben, gibt der Raumschiffoperator grundsätzlich zurück, je nachdem, ob die Argumente gleich oder ungleich 1sind 0, oder −1abhängig vom Wert des linken Arguments relativ zum rechten Argument.

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

Dies ist nützlich zum Sortieren eines Arrays.

TonyArra
quelle
27
Genau. Ich halte es für eine sehr elegante Version von Java's Comparable.
Mike Reedell
12
analog in c # ist IComparable.CompareTo
Sergey Mirvoda
1
Eigentlich denke ich, dass jeder negative oder positive Wert zurückgegeben werden kann. 0 bedeutet immer noch Gleichheit.
Superluminary
1
@superluminary Im Gegensatz zur strcmp-Funktion von C wurde x <=> y ​​speziell entwickelt, um nur -1, 0, 1 oder nil zurückzugeben, wenn x und y nicht vergleichbar sind (in Ruby und allen anderen Sprachen, die AFAIK verwenden). Dies macht es einfach, den Bediener zu überlasten, beispielsweise für Ruby's Comparable Mixin. In Perl, wo der Operator höchstwahrscheinlich seinen Ursprung hat, wurde er hauptsächlich verwendet, um die Syntax "sort BLOCK LIST" zu vereinfachen. Der BLOCK ist eine Unterroutine, die je nach Sortierung der Listenelemente eine beliebige positive, negative oder 0-Zahl zurückgeben kann. Der Raumschiffoperator ist bequem im Block zu verwenden.
TonyArra
2
Beachten Sie, dass , wenn die beiden Objekte im Vergleich nicht vergleichbar sind, erhalten Sie eine Null
Gamov
70

Die Raumschiffmethode ist nützlich, wenn Sie sie in Ihrer eigenen Klasse definieren und das Modul Vergleichbar einschließen . Ihre Klasse erhält dann die >, < , >=, <=, ==, and between?Methoden kostenlos.

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

Es ist ein allgemeiner Vergleichsoperator. Es gibt entweder -1, 0 oder +1 zurück, je nachdem, ob sein Empfänger kleiner, gleich oder größer als sein Argument ist.

gnovice
quelle
18

Ich werde mit einem einfachen Beispiel erklären

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

    Ruby beginnt mit dem Vergleich jedes Elements beider Arrays von der linken Seite. 1für das linke Array ist kleiner als 2für das rechte Array. Daher ist das linke Array kleiner als das rechte Array. Ausgabe wird sein -1.

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

    Wie oben wird zuerst das erste Element verglichen, das gleich ist, dann wird das zweite Element verglichen. In diesem Fall ist das zweite Element des linken Arrays größer, daher ist die Ausgabe 1.

Anil Maurya
quelle
vergleicht es nur das erste linke Element jedes Arrays oder vergleicht es auch weiterhin andere Elemente? gute Erklärung
Kick Buttowski
1
@KickButtowski vergleicht weiterhin andere Elemente, es sei denn, es wird eine ungleiche Zahl gefunden.
Anil Maurya
5

Da dieser Operator Vergleiche auf einen ganzzahligen Ausdruck reduziert, bietet er die allgemeinste Möglichkeit, aufsteigend oder absteigend basierend auf mehreren Spalten / Attributen zu sortieren.

Wenn ich beispielsweise eine Reihe von Objekten habe, kann ich Folgendes tun:

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

Dieses Grundmuster kann verallgemeinert werden, um nach einer beliebigen Anzahl von Spalten in einer beliebigen Permutation von aufsteigend / absteigend zu sortieren.

Lilole
quelle
Schöne Beispiele, nur dass das letzte nicht wie erwartet funktioniert. Die Faktoren sollten Zweierpotenzen in absteigender Reihenfolge sein, dh 8, -4, 2, 1. Die Art und Weise, wie Sie es geschrieben haben (mit den Faktoren 4, -3,2,1), z. B. "Alter + Nachname" zählt mehr als "zip" "...
Elmar Zander
Ich glaube nicht, dass diese Zahlen bedeuten, was Sie denken, dass sie bedeuten. Jeder Faktor multipliziert das Signum, das -1, 0 oder 1 ist. Potenzen von 2 spielen hier keine Rolle. Das -3 * (a.age <=> b.age) ist genau das gleiche wie 3 * (b.age <=> a.age). Das Vorzeichen des Ergebnisses ist, was es auf- oder absteigen lässt.
Lilole
Nein, es ist sehr wichtig. Der Faktor für die Postleitzahl muss größer sein als die (absolute) Summe aller anderen Faktoren, und der Faktor für das Alter muss größer sein als die (absolute) Summe der Faktoren für den letzten und den ersten und so weiter. Und die kleinste Folge von Zahlen, die das erfüllt, ist die Folge von Zweierpotenzen ... Und übrigens, wenn Sie meinen Kommentar sorgfältig gelesen hätten, hätten Sie gesehen, dass ich das Minuszeichen eingefügt habe ...
Elmar Zander
1
Ok, vielleicht werde ich etwas näher darauf eingehen: Mit den Faktoren (4, -3,2,1) und den Ergebnissen des Raumschiffs op (1,1, -1, -1) ist die gewichtete Summe -2, aber es muss positiv sein! Andernfalls kommt der größere Reißverschluss vor dem kleineren Reißverschluss. Dies wird mit Faktoren (8, -4,2,1) nicht geschehen.
Elmar Zander
1
Ah, ich sehe jetzt, wenn nach> 2 Spalten sortiert wird, sind die Potenzen von 2 erforderlich. Vielen Dank, dass Sie dabei geholfen haben, dies zu korrigieren. Sorry Welt, wenn deine 3 oder mehr Spalten-Sortierung falsch ausfiel.
Lilole
-2

Was ist <=> (der Raumschiff-Betreiber)

Laut dem RFC, der den Operator eingeführt hat , ist $ 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

Beispiel:

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

MEHR:

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