传递块时Array#sort如何工作?


76

我在理解如何array.sort{ |x,y| block }正确工作方面存在问题,因此如何使用它?

Ruby文档中的一个示例:

   a = [ "d", "a", "e", "c", "b" ]
   a.sort                     #=> ["a", "b", "c", "d", "e"]
   a.sort { |x,y| y <=> x }   #=> ["e", "d", "c", "b", "a"]

Answers:


125

在你的例子中

a.sort

相当于

a.sort { |x, y| x <=> y }

如你所知,对数组排序,你需要能够比较它的元素(如果你怀疑,只是试图实现任何排序算法,而无需使用任何比较,没有<><=>=)。

您提供的块实际上是一个函数,sort算法将调用该函数以比较两个项目。那就是x并且y将永远是由sort算法在执行过程中。

sort算法将假定此比较功能/块将满足方法的要求<=>

  • 如果x <y返回-1
  • 如果x = y返回0
  • 如果x> y返回1

无法提供足够的比较功能/块将导致数组的顺序不确定。

您现在应该明白为什么

a.sort { |x, y| x <=> y }

a.sort { |x, y| y <=> x }

以相反的顺序返回相同的数组。


要详细说明泰特·约翰逊(Tate Johnson)添加的内容,如果<=>对任何课程实施比较功能,都将获得以下内容

  1. 您可以包括模块Comparable中的类,它会自动为您定义了以下方法:between?==>=<<=>
  2. 现在可以使用对的默认调用(即不带参数)对类的实例进行排序sort

请注意,<=>已经提供的方法无论它在Ruby的标准库是有道理的(BignumArrayFile::StatFixnumStringTime,等...)。


1
我认为值得添加的<=>是定义并Comparable与混合使用的方法StringFixnumBignum定义<=>。您可以<=>在需要排序或比较的任何类中实施。
塔特·约翰逊

我强调了重要的一句话。x和y将是数组的2个元素,由排序算法本身选择。
bltxd 2010年

1
还需要注意的是,您的代码块可以根据需要尽可能复杂或深入。只要返回值准确反映您期望的<=>运算符的行为,您就可以执行所需的任何操作。因此,如果要基于布尔值或其他值进行排序,则可以评估该布尔值并相应地返回-1或1。它派上用场了。
马修2010年

FWIW,<=>被称为太空飞船运营商
B 2014年

22

当您有一个要排序的整数数组时,sort方法可以很容易地对元素进行正确排序-首先是较小的数字,最后是较大的数字。那是当你用普通的sort,没有任何障碍。

但是,当您对其他对象进行排序时,可能需要提供一种比较(每个)两个对象的方法。假设您有一个class对象的数组Person。您可能无法判断对象bob是否大于对象mike(即,类Person<=>实现方法)。在这种情况下,您需要提供一些代码来说明您希望这些对象按哪种顺序排序sort。那就是块形式的开始。

people.sort{|p1,p2| p1.age <=> p2.age}
people.sort{|p1,p2| p1.children.count <=> p2.children.count}

在所有这些情况下,sort方法对它们的排序方式相同-使用相同的算法。比较逻辑是不同的。


说实话,发现这个答案更有帮助。图片讲一千个单词,一个例子讲一千条解释。
凯文·蒙克

注意:people.sort{|p1,p2| p1.age <=> p2.age}可以改写为people.sort_by{|p| p.age}
Cyoce '16

9

@OscarRyz的回复对我进行了很多整理,尤其是关于排序方式的问题

 { |x, y| y <=> x }

根据我的理解,我在此提供上述块结果的每次比较之后数组的状态。

注意:获得了从Ruby论坛打印块参数e1,e2的值的参考

1.9.3dev :001 > a = %w(d e a w f k)
1.9.3dev :003 > a.sort { |e1, e2| p [e2, e1]; e2 <=> e1 }
["w", "d"]
["k", "w"]
["k", "d"]
["k", "e"]
["k", "f"]
["k", "a"]
["f", "a"]
["d", "f"]
["d", "a"]
["d", "e"]
["e", "f"]
 => ["w", "k", "f", "e", "d", "a"]

每次比较后在运行时的猜测数组状态:

 [e2, e1]    Comparsion Result       Array State
["w", "d"]      1                   ["w", "e", "a", "d", "f", "k"]
["k", "w"]     -1                   ["w", "e", "a", "d", "f", "k"]
["k", "d"]      1                   ["w", "e", "a", "k", "f", "d"]
["k", "e"]      1                   ["w", "k", "a", "e", "f", "d"]  
["k", "f"]      1                   ["w", "k", "a", "e", "f", "d"]    
["k", "a"]      1                   ["w", "k", "a", "e", "f", "d"]  
["f", "a"]      1                   ["w", "k", "f", "e", "a", "d"]  
["d", "f"]     -1                   ["w", "k", "f", "e", "a", "d"]  
["d", "a"]      1                   ["w", "k", "f", "e", "d", "a"]  
["d", "e"]     -1                   ["w", "k", "f", "e", "d", "a"]  
["e", "f"]     -1                   ["w", "k", "f", "e", "d", "a"] (Result)

谢谢,

吉涅什


7

<=>是一个返回(self.<=>( argument ))的方法

  • 如果self <参数,则为-1
  • 如果self ==参数为0
  • 如果self>参数为1

xy是数组项。如果未提供任何块,则该sort函数使用x<=>y,否则该块的结果表明x是否应位于y之前。

array.sort{|x, y| some_very_complicated_method(x, y) }

如果some_very_complicated_method(x,y)返回的smth <0,则认为x小于y,依此类推...


4

一些杂点:

  • x并且y称为块参数。排序方法基本上说:“我给您x和y,您确定x或y应该先出现,然后我会照顾无聊的排序问题”
  • <=>被称为太空船运营商

3

在:

a.sort {|x,y| y <=> x }   #=> ["e", "d", "c", "b", "a"]

x和y是什么?

xy被所述元件通过排序算法进行比较。

这对于为自定义类定义哪个元素应该位于另一个元素之前很有用。

对于基本数据(数字,字符串,日期等),自然顺序是预定义的,但是对于客户元素(即Employee),您可以定义在比较中谁先于谁。该块为您提供了定义的机会。

y <=> x会发生什么?

在那里,他们正在按降序比较元素(值“较高”的元素排在第一),而不是自然顺序(x<=>y

<=>方法代表“的compareTo”,返回0,如果元素是等价的,或者<0,如果x那张比以前y>如果0x后进入y


2

我相信| x,y | y <=> x一次按降序比较两个元素,如下所示:http : //www.ruby-doc.org/core-1.9.3/Array.html#method-i-3C-3D- 3E 说[[d],“ a”,“ e”,“ c”,“ b”],“ d”和“ a”似乎先进行比较。然后,由于下降,由于d的值小于a,因此两者保持相同的顺序。然后评估d和e。“ e”移动到“ d”的位置。在不了解c代码内部工作的情况下,无法知道将其移动到了哪里,但是我认为这个过程将一直持续到对所有元素进行排序为止。C函数:

           VALUE
rb_ary_cmp(VALUE ary1, VALUE ary2)
{
    long len;
    VALUE v;

    ary2 = rb_check_array_type(ary2);
    if (NIL_P(ary2)) return Qnil;
    if (ary1 == ary2) return INT2FIX(0);
    v = rb_exec_recursive_paired(recursive_cmp, ary1, ary2, ary2);
    if (v != Qundef) return v;
    len = RARRAY_LEN(ary1) - RARRAY_LEN(ary2);
    if (len == 0) return INT2FIX(0);
    if (len > 0) return INT2FIX(1);
    return INT2FIX(-1);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.