怎么< =>为不同的分类策略工作?

时间:2013-11-27 15:55:11

标签: ruby

我正在阅读有关CodeAcademy的一些教程,并遇到了这种情况:

books = ["Charlie and the Chocolate Factory", "War and Peace", "Utopia", "A Brief History of Time", "A Wrinkle in Time"]

# To sort our books in ascending order, in-place
books.sort! { |firstBook, secondBook| firstBook <=> secondBook }


# Sort your books in descending order, in-place below
# this lin initially left blank
books.sort! {|firstBook, secondBook| secondBook <=> firstBook}

我没有使用if / else块,而是给了它一个镜头,它有效,但我不知道为什么。我假设您将项目放在支票中的顺序无关紧要(即a <=> bb <=> a)。有人能解释一下这里发生了什么吗?

3 个答案:

答案 0 :(得分:5)

如果您反转<=>中的元素,则会反转其值。如果元素相等,则此运算符返回0,但如果第一个较小则返回负值,如果第一个较大则返回正值。因此,如果temp = a <=> bb <=> a-temp。因此,如果以相反的顺序编写参数,则反转排序顺序。

答案 1 :(得分:3)

以下是一些简单的视觉方式,可以看到<=>的作用,以及如何反转比较变量的顺序会影响输出的顺序。

从基本数组开始:

foo = %w[a z b x]

我们可以进行升序排序:

foo.sort { |i, j| i <=> j } # => ["a", "b", "x", "z"]

或者通过反转被比较的两个变量来降序排列:

foo.sort { |i, j| j <=> i } # => ["z", "x", "b", "a"]

<=>运算符返回-1,0或1,具体取决于比较分别是<==还是>

我们可以通过否定比较结果来测试,如果理论成立,它将颠倒顺序。

foo.sort { |i, j| -(i <=> j) } # => ["z", "x", "b", "a"]
foo.sort { |i, j| -(j <=> i) } # => ["a", "b", "x", "z"]

通过否定比较结果,订单反转。但是,为了清晰起见,只需改变变量的顺序即可。

所有人都说,使用sort或其破坏性兄弟sort!并不总是排序复杂对象的最快方法。简单的对象,如字符串和字符,以及数字,排序非常快,因为它们的类实现了快速执行<=>测试的必要方法。

有些答案和评论提及sort_by,所以让我们去那里。

复杂对象通常不能正确排序,因此我们最终使用getter / accessors来检索我们想要比较的值,并且该操作的CPU时间成本。 sort重复比较这些值,以便重复进行检索,并将其作为未进行排序时的浪费时间加起来。

为了解决这个问题,一个名叫Randall Schwartz的聪明人,他是Perl世界的主要参与者,开始使用一种算法,该算法预先计算一次用于排序的值;该算法通常称为Schwartzian Transform。该值和实际对象在一个小的子数组中捆绑在一起,然后进行排序。因为排序是针对预先计算的值而发生的,所以它和它的关联对象在排序中移动,直到排序完成。此时,将检索实际对象并将其作为方法的结果返回。 Ruby使用sort_by实现了这种类型的排序。

sort_by不会在外部使用<=>,因此您可以通过简单地告诉它如何获取您想要比较的值来进行排序:

class Foo
  attr_reader :i, :c
  def initialize(i, c)
    @i = i
    @c = c
  end
end

这是对象数组。请注意,它们按创建顺序排列,但未排序:

foo = [[1,  'z'], [26, 'a'], [2,  'x'], [25, 'b'] ].map { |i, c| Foo.new(i, c) }
# => [#<Foo:0x007f97d1061d80 @c="z", @i=1>,
#     #<Foo:0x007f97d1061d58 @c="a", @i=26>,
#     #<Foo:0x007f97d1061d30 @c="x", @i=2>,
#     #<Foo:0x007f97d1061ce0 @c="b", @i=25>]

按整数值排序:

foo.sort_by{ |f| f.i } 
# => [#<Foo:0x007f97d1061d80 @c="z", @i=1>,
#     #<Foo:0x007f97d1061d30 @c="x", @i=2>,
#     #<Foo:0x007f97d1061ce0 @c="b", @i=25>,
#     #<Foo:0x007f97d1061d58 @c="a", @i=26>]

按字符值排序:

foo.sort_by{ |f| f.c } 
# => [#<Foo:0x007f97d1061d58 @c="a", @i=26>,
#     #<Foo:0x007f97d1061ce0 @c="b", @i=25>,
#     #<Foo:0x007f97d1061d30 @c="x", @i=2>,
#     #<Foo:0x007f97d1061d80 @c="z", @i=1>]

sort_by对使用sort<=>的否定值没有反应,因此,基于some benchmarks在Stack Overflow上做了一段时间,我们知道在结果值上使用reverse是将顺序从升序切换到降序的最快方法:

foo.sort_by{ |f| f.i }.reverse
# => [#<Foo:0x007f97d1061d58 @c="a", @i=26>,
#     #<Foo:0x007f97d1061ce0 @c="b", @i=25>,
#     #<Foo:0x007f97d1061d30 @c="x", @i=2>,
#     #<Foo:0x007f97d1061d80 @c="z", @i=1>]

foo.sort_by{ |f| f.c }.reverse 
# => [#<Foo:0x007f97d1061d80 @c="z", @i=1>,
#     #<Foo:0x007f97d1061d30 @c="x", @i=2>,
#     #<Foo:0x007f97d1061ce0 @c="b", @i=25>,
#     #<Foo:0x007f97d1061d58 @c="a", @i=26>]

它们有点可以互换,但你必须记住sort_by确实有开销,这在你对简单对象运行时将它的时间与sort次进行比较时很明显。在正确的时间使用正确的方法,你可以看到戏剧性的加速。

答案 2 :(得分:0)

Its called a spaceship operator

如果你有类似的东西

my_array = ["b","c","a"]

my_array.sort!比较数组的元素,因为它知道英文字母的字母有自然顺序,同样如果你有整数数组

my_array2 = [3,1,2]

my_array2.sort!将比较元素并将结果显示为[1,2,3]

但如果您想更改字符串数组或复杂对象的比较方式,请使用<=>运算符指定它。

my_array3 = ["hello", "world how are" , "you"]

my_array3.sort! { |first_element, second_element| first_element <=> second_element }

所以它会告诉sort方法比较如下:

first_element&lt; second_element

first_element = second_element

first_element&gt; second_element

但如果你采取这个措施​​,

my_array3.sort! { |first_element, second_element| first_element <=> second_element }

比较如下:

是second_element&lt; first_element?

是second_element = first_element?

是second_element&gt; first_element?

如果你改变要考虑的元素,它确实会有所作为。