Что такое оператор Ruby <=> (космический корабль)?

263

Что такое <=>оператор Ruby (космический корабль)? Реализован ли оператор на каких-либо других языках?

Джастин этир
источник
1
А как насчет сравнения массивов? В книге сказано: «сравнивает элемент за элементом, возвращает 0, если равен, -1, если меньше, 1, если больше, но как насчет [1,3,2] <=> [2,2,2]
SF.
3
@SF, когда люди сравнивают массивы, они обычно подразумевают сравнение лексикографически (как в словаре, то есть [1,3,2] <[2,2,2], потому что первые элементы отличаются). Редко (например, в Matlab) сравнение массивов возвращает массив результатов на элемент; в этом случае: [-1, 1, 0].
Лиори
Обратите внимание, что массивы, которые содержат нулевые элементы, сравнимы, если элементы перед любым нулем различны, и не сравнимы, если нулевое значение должно сравниваться с ненулевым. Т.е. [1, ноль] <=> [2, 3] => -1, но [1, ноль] <=> [1, 3] => ноль. Это отстой, в основном.
Клиффордхет
При сравнении таких массивов [1,nil] <=> [1,3]вы получаете nilиз-за непротиворечивости алгоритма, сравнивая каждый элемент по очереди, пока <=>результат НЕ будет 0. В этом примере Ruby не может объявить меньше или больше, чем просто, поскольку сравнение просто невозможно. К ним nilследует относиться как к «не равным». Если вы знаете что - то о данных, и , например , хотите , чтобы лечить , nilкак 0, Ruby делает это легко.
Lilole

Ответы:

360

Perl был, вероятно, первым языком, который использовал это. Groovy - это другой язык, который его поддерживает. В принципе , вместо того , чтобы вернуться 1( true) или 0( false) в зависимости от того , аргументы равные или неравные, оператор космического корабля вернется 1, 0или в −1зависимости от значения левого аргумента по отношению к правому аргументу.

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

Это полезно для сортировки массива.

TonyArra
источник
27
Именно. Я считаю его очень элегантной версией Java Comparable.
Майк Риделл
12
аналог в c # является IComparable.CompareTo
Сергей Мирвода
1
На самом деле я думаю, что любое отрицательное или положительное значение может быть возвращено. 0 по-прежнему означает равенство.
Сверхсветовой
1
@superluminary В отличие от функции C strcmp, функция x <=> y ​​специально предназначена для возврата -1, 0, 1 или nil, если x и y несопоставимы (в Ruby и любых других языках, использующих его AFAIK). Это облегчает перегрузку оператора, например, в Ruby Comparable mixin. В Perl, где оператор, скорее всего, возник, он использовался главным образом для упрощения синтаксиса «сортировать BLOCK LIST». BLOCK - это подпрограмма, которая может вернуть любое положительное число, отрицательное число или 0 в зависимости от того, как элементы списка должны быть отсортированы. Космический корабль оператора удобно использовать в блоке.
ТониАрра
2
Обратите внимание, что если два сравниваемых объекта не сопоставимы, вы получите ноль
gamov
70

Метод космического корабля полезен, когда вы определяете его в своем собственном классе и включаете модуль Comparable . Ваш класс получает >, < , >=, <=, ==, and between?методы бесплатно.

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
источник
20

Это общий оператор сравнения. Он возвращает либо -1, 0, либо +1 в зависимости от того, меньше ли его получатель, равен или больше его аргумента.

gnovice
источник
18

Я объясню на простом примере

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

    Ruby начнет сравнивать каждый элемент обоих массивов с левой стороны. 1для левого массива меньше, чем 2для правого массива. Следовательно, левый массив меньше правого. Выходной будет -1.

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

    Как и выше, он сначала сравнивает первый элемент, который равен, затем он сравнивает второй элемент, в этом случае второй элемент левого массива больше, следовательно, вывод равен 1.

Анил Маурья
источник
он просто сравнивает первый левый элемент каждого массива или продолжает сравнивать и другие элементы? хорошее объяснение
Kick Buttowski
1
@ KickButtowski он продолжает сравнивать другие элементы, если не находит неравное число.
Анил Маурья
5

Поскольку этот оператор сводит сравнения к целочисленному выражению, он предоставляет наиболее общий способ сортировки по возрастанию или убыванию на основе нескольких столбцов / атрибутов.

Например, если у меня есть массив объектов, я могу делать такие вещи:

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

Этот базовый шаблон можно обобщить для сортировки по любому количеству столбцов в любой перестановке по возрастанию / убыванию каждого.

lilole
источник
Хорошие примеры, только последний не работает, как ожидалось. Факторами должны быть степени двойки в порядке убывания, то есть 8, -4, 2, 1. То, как вы это написали (с коэффициентами 4, -3,2,1), например, «возраст + фамилия», имеет значение больше, чем «zip» "...
Эльмар Цандер
Я не думаю, что эти цифры означают то, что вы думаете, они имеют в виду. Каждый фактор умножает сигнум, который будет равен -1, 0 или 1. Степени 2 здесь не имеют значения. -3 * (a.age <=> b.age) в точности совпадает с 3 * (b.age <=> a.age). Признаком результата является то, что делает его asc или desc.
Лилол
Нет, это имеет большое значение. Коэффициент для индекса должен быть больше, чем (абсолютная) сумма всех других факторов, а коэффициент для возраста должен быть больше, чем (абсолютная) сумма факторов последнего и первого и т. Д. И наименьшая последовательность чисел, которая соответствует этой последовательности степеней двух ... И кстати, если бы вы внимательно прочитали мой комментарий, вы бы увидели, что я включил знак минус ...
Эльмар Цандер
1
Хорошо, может быть, я более подробно остановлюсь на этом: с коэффициентами (4, -3,2,1) и результатами космического корабля op (1,1, -1, -1) взвешенная сумма равна -2, но это должно быть позитивно! В противном случае больший почтовый индекс появится перед меньшим почтовым индексом. Это не произойдет с факторами (8, -4,2,1).
Эльмар Цандер
1
Ах, теперь я вижу, если сортировка по> 2 столбцам, то требуется степень 2. Спасибо за помощь, чтобы исправить это. Извините, если ваша сортировка по 3 или более столбцам оказалась неверной.
Лилол
-2

Что такое <=> (оператор "Космического корабля")

Согласно RFC, который представил оператора , $ 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

Пример:

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

БОЛЬШЕ:

// 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
РишиКеш Кумар
источник