Qu'est-ce que l'opérateur <=> Ruby (vaisseau spatial)?

263

Qu'est-ce que l' <=>opérateur Ruby (vaisseau spatial)? L'opérateur est-il implémenté dans d'autres langues?

Justin Ethier
la source
1
Qu'en est-il maintenant de la comparaison des tableaux? Il a dit dans le livre "compare élément par élément, renvoie 0 si égal, -1 si inférieur, 1 si supérieur, mais qu'en est-il [1,3,2] <=> [2,2,2]?
SF.
3
@SF, lorsque les gens comparent des tableaux, ils veulent généralement comparer lexicographiquement (comme dans un dictionnaire, c'est-à-dire [1,3,2] <[2,2,2] parce que les premiers éléments sont différents). Rarement (fe dans Matlab), la comparaison de tableaux renvoie un tableau de résultats par élément; dans ce cas: [-1, 1, 0].
liori
Notez que les tableaux qui contiennent des éléments nil sont comparables si les éléments avant tout nil sont différents, et non comparables si un nil doit être comparé avec un non-nil. C'est-à-dire [1, néant] <=> [2, 3] => -1, mais [1, néant] <=> [1, 3] => nul. Cela craint, fondamentalement.
cliffordheath
Lorsque vous comparez des tableaux comme [1,nil] <=> [1,3]vous obtenez un en nilraison de la cohérence de l'algorithme, comparez tour à tour chaque élément jusqu'à ce que le <=>résultat ne soit PAS 0. Il n'y a aucun moyen pour Ruby de déclarer moins ou plus que dans cet exemple, car une comparaison ne peut tout simplement pas être faite. Le nildoit être traité comme "non égal". Si vous savez quelque chose sur les données et que vous souhaitez par exemple les traiter nilcomme telles 0, Ruby vous facilite la tâche.
lilole

Réponses:

360

Perl était probablement la première langue à l'utiliser. Groovy est une autre langue qui le prend en charge. Fondamentalement , au lieu de retourner 1( true) ou 0( false) selon que les arguments sont égaux ou inégaux, l'opérateur de vaisseau spatial retourne 1, 0ou en −1fonction de la valeur de l'argument gauche par rapport à l'argument droit.

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

Il est utile pour trier un tableau.

TonyArra
la source
27
Exactement. Je pense que c'est une version très élégante du Comparable de Java.
Mike Reedell
12
analogique en c # est IComparable.CompareTo
Sergey Mirvoda
1
En fait, je pense que toute valeur négative ou positive peut être retournée. 0 signifie toujours égalité.
superluminaire
1
@superluminary Contrairement à la fonction strcmp de C, x <=> y ​​est conçu spécifiquement pour renvoyer uniquement -1, 0, 1 ou nil si x et y ne sont pas comparables (en Ruby et dans les autres langages qui l'utilisent AFAIK). Cela facilite la surcharge de l'opérateur, comme pour le mixin Comparable de Ruby. En Perl, où l'opérateur est vraisemblablement originaire, il a été utilisé principalement pour simplifier la syntaxe "trier BLIST LIST". Le BLOC est un sous-programme qui peut renvoyer n'importe quel nombre positif, négatif ou 0 selon la façon dont les éléments de la liste doivent être triés. L'opérateur de vaisseau spatial est pratique à utiliser dans le bloc.
TonyArra
2
Notez que si les deux objets comparés ne sont pas comparables, vous obtenez un nul
gamov
70

La méthode du vaisseau spatial est utile lorsque vous la définissez dans votre propre classe et incluez le module Comparable . Votre classe obtient ensuite les >, < , >=, <=, ==, and between?méthodes gratuitement.

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
la source
20

C'est un opérateur de comparaison général. Il renvoie soit -1, 0 ou +1 selon que son récepteur est inférieur, égal ou supérieur à son argument.

gnovice
la source
18

Je vais vous expliquer avec un exemple simple

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

    Ruby commencera à comparer chaque élément des deux tableaux du côté gauche. 1pour le tableau de gauche est plus petit que pour le tableau 2de droite. Par conséquent, le tableau de gauche est plus petit que le tableau de droite. La sortie sera -1.

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

    Comme ci-dessus, il comparera d'abord le premier élément qui est égal, puis il comparera le deuxième élément, dans ce cas, le deuxième élément du tableau de gauche est plus grand, d'où la sortie 1.

Anil Maurya
la source
compare-t-il simplement le premier élément gauche de chaque tableau ou continue-t-il également à comparer d'autres éléments? bonne explication
Kick Buttowski
1
@KickButtowski continue de comparer d'autres éléments à moins qu'il ne trouve un nombre inégal.
Anil Maurya
5

Étant donné que cet opérateur réduit les comparaisons à une expression entière, il fournit le moyen le plus général de trier par ordre croissant ou décroissant en fonction de plusieurs colonnes / attributs.

Par exemple, si j'ai un tableau d'objets, je peux faire des choses comme ceci:

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

Ce modèle de base peut être généralisé pour trier par n'importe quel nombre de colonnes, dans n'importe quelle permutation ascendante / descendante sur chacune.

lilole
la source
De bons exemples, juste que le dernier ne fonctionne pas comme prévu. Les facteurs doivent être des puissances de deux dans l'ordre décroissant, c'est-à-dire 8, -4, 2, 1. La façon dont vous l'avez écrit (avec les facteurs 4, -3,2,1), par exemple "âge + nom" compte plus que "zip "...
Elmar Zander
Je ne pense pas que ces chiffres signifient ce que vous pensez qu'ils signifient. Chaque facteur multiplie le signum, qui sera -1, 0 ou 1. Les puissances de 2 n'ont pas d'importance ici. Le -3 * (a.age <=> b.age) est exactement le même que 3 * (b.age <=> a.age). Le signe du résultat est ce qui le fait monter ou descendre.
lilole
Non, ça compte beaucoup. Le facteur pour zip doit être supérieur à la somme (absolue) de tous les autres facteurs, et le facteur pour l'âge doit être supérieur à la somme (absolue) des facteurs de dernier et premier, et ainsi de suite. Et la plus petite séquence de nombres qui remplit c'est la séquence de pouvoirs de deux ... Et BTW si vous lisez attentivement mon commentaire, vous auriez vu que j'ai inclus le signe moins ...
Elmar Zander
1
Ok, je vais peut-être développer un peu plus à ce sujet: avec les facteurs (4, -3,2,1) et les résultats du vaisseau spatial op (1,1, -1, -1) la somme pondérée est -2, mais il faut que ce soit positif! Sinon, le plus grand zip viendra avant le plus petit. Cela ne se produira pas avec des facteurs (8, -4,2,1).
Elmar Zander
1
Ah je vois maintenant, si le tri sur> 2 colonnes alors les puissances de 2 sont nécessaires. Merci d'avoir aidé à corriger cela. Désolé, si le tri de vos colonnes ou plus s'est avéré incorrect.
lilole
-2

Qu'est-ce que <=> (l'opérateur «vaisseau spatial»)

Selon le RFC qui a présenté l'opérateur , $ 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

Exemple:

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

PLUS:

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