什么是 Ruby <=>(宇宙飞船)运算符?

Posted

技术标签:

【中文标题】什么是 Ruby <=>(宇宙飞船)运算符?【英文标题】:What is the Ruby <=> (spaceship) operator? 【发布时间】:2010-10-24 01:56:58 【问题描述】:

什么是 Ruby &lt;=&gt;(宇宙飞船)运算符?运算符是否由其他语言实现?

【问题讨论】:

现在比较数组呢?它在书中说“逐个元素比较,如果相等则返回0,如果较小则返回-1,如果较大则返回1,但是[1,3,2] &lt;=&gt; [2,2,2]呢? @SF,当人们比较数组时,他们通常意味着按字典顺序比较(就像在字典中,即 [1,3,2] 请注意,如果任何 nil 之前的元素不同,则包含 nil 元素的数组是可比较的,如果必须将 nil 与非 nil 进行比较,则不可比较。 IE。 [1, nil] [2, 3] => -1,但是 [1, nil] [1, 3] => nil。基本上,这很糟糕。 当比较像[1,nil] &lt;=&gt; [1,3]这样的数组时,由于算法的一致性,你会得到一个nil,依次比较每个元素,直到&lt;=&gt;的结果不是0。在此示例中,Ruby 无法声明小于或大于,因为根本无法进行比较。 nil 应被视为“不相等”。如果您对数据有所了解,例如想要将nil 视为0,Ruby 让这很容易。 【参考方案1】:

它也被称为三路比较运算符。 Perl 可能是第一种使用它的语言。支持它的其他一些语言是:Apache Groovy、php 7+ 和 C++20。

根据参数是否相等,而不是返回 1 (true) 或 0 (false),宇宙飞船运算符将返回 10−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

常用于数据排序。

【讨论】:

没错。我认为它是 Java 的 Comparable 的一个非常优雅的版本。 c#中的类比是IComparable.CompareTo 其实我认为可以返回任何负值或正值。 0 仍然意味着平等。 @superluminary 与 C 的 strcmp 函数不同,x y 专门设计为仅在 x 和 y 不可比较时才返回 -1、0、1 或 nil(在 Ruby 和任何其他语言中使用它AFAIK)。这使得重载运算符变得很容易,例如 Ruby 的 Comparable mixin。在 Perl 中,运算符最有可能起源于此,它主要用于简化“排序块列表”语法。 BLOCK 是一个子例程,它可以根据列表项的排序方式返回任何正数、负数或 0。宇宙飞船算子在区块中使用很方便。 请注意,如果比较的两个对象不具有可比性,则会得到 nil【参考方案2】:

当您在自己的类中定义 spaceship 方法并包含 Comparable module 时,它很有用。然后,您的班级将免费获得 &gt;, &lt; , &gt;=, &lt;=, ==, 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>]

【讨论】:

【参考方案3】:

这是一个通用的比较运算符。它返回 -1、0 或 +1,具体取决于其接收者是小于、等于还是大于其参数。

【讨论】:

【参考方案4】:

我会用简单的例子来解释

    [1,3,2] &lt;=&gt; [2,2,2]

    Ruby 将从左侧开始比较两个数组的每个元素。 左侧数组的1 小于右侧数组的2。因此左数组小于右数组。输出将是-1

    [2,3,2] &lt;=&gt; [2,2,2]

    如上所述,它将首先比较相等的第一个元素,然后比较第二个元素,在这种情况下,左数组的第二个元素更大,因此输出为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

这种基本模式可以推广到按任意数量的列进行排序,在每个列上以任何升序/降序排列。

【讨论】:

很好的例子,只是最后一个没有按预期工作。因子应该是按降序排列的 2 的幂,即 8、-4、2、1。您编写它的方式(使用因子 4、-3、2、1),例如“年龄+姓氏”比“zip”更重要... 我认为这些数字并不像您认为的那样。每个因子都会乘以符号,即 -1、0 或 1。2 的幂在此无关紧要。 -3 * (a.age b.age) 与 3 * (b.age a.age) 完全相同。结果的符号决定了它是升序还是降序。 不,这确实很重要。 zip 的因子必须大于所有其他因子的(绝对)总和,age 的因子必须大于 last 和 first 的因子的(绝对)总和,以此类推。满足这一要求的最小数字序列是 2 的幂序列......顺便说一句,如果你仔细阅读我的评论,你会看到我包含了减号...... 好的,也许我会详细说明一下:因子 (4,-3,2,1) 和宇宙飞船操作的结果 (1,1,-1,-1)加权和是-2,但它必须是正数!否则较大的拉链会在较小的拉链之前出现。因数 (8,-4,2,1) 不会发生这种情况。 啊,我现在明白了,如果对 > 2 列进行排序,则需要 2 的幂。感谢您帮助纠正此问题。抱歉,如果您的 3 列或更多列排序错误。【参考方案6】:

什么是&lt;=&gt;(“宇宙飞船”操作员)

根据RFC that introduced the operator,$a &lt;=&gt;$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

【讨论】:

以上是关于什么是 Ruby <=>(宇宙飞船)运算符?的主要内容,如果未能解决你的问题,请参考以下文章

ruby Ruby:宇宙飞船

PHP 7 中的 <=> (“宇宙飞船”运算符)是啥? [复制]

宇宙最正红色——MAC Ruby Woo

在 ruby​​ 循环的结束标记中添加连字符是啥意思 <% -%>

Ruby on Rails 中的 Runner

strcmp() 和 Spaceship Operator (<=>) 有啥区别