【问题标题】:How does Array#sort work when a block is passed?传递块时 Array#sort 如何工作?
【发布时间】:2011-02-07 21:47:25
【问题描述】:

我无法理解 array.sort{ |x,y| block } 的确切工作原理,因此如何使用它?

来自Ruby documentation的例子:

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

【问题讨论】:

    标签: arrays ruby sorting


    【解决方案1】:

    在你的例子中

    a.sort
    

    等价于

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

    如您所知,要对数组进行排序,您需要能够比较它的元素(如果您对此表示怀疑,请尝试在不使用任何比较的情况下实现任何排序算法,没有&lt;&gt;、@987654325 @ 或 &gt;=)。

    您提供的块实际上是一个函数,将由sort 算法调用以比较两个项目。 xy 将始终是sort 算法在执行期间选择的输入数组的某些元素。

    sort 算法将假定此比较函数/块将满足方法 &lt;=&gt; 的要求:

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

    未能提供足够的比较函数/块将导致数组的顺序未定义。

    你现在应该明白为什么了

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

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

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


    为了详细说明 Tate Johnson 添加的内容,如果您在任何类上实现比较函数 &lt;=&gt;,您将获得以下内容

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

    请注意,&lt;=&gt; 方法已在 ruby​​ 标准库中任何有意义的地方提供(BignumArrayFile::StatFixnumStringTime 等... )。

    【讨论】:

    • 我认为值得补充的是,&lt;=&gt; 是在Comparable 中定义并与String 混合的方法。 FixnumBignum 也定义了 &lt;=&gt;。您可以在任何需要排序或比较的类中实现&lt;=&gt;
    • 我强调了重要的句子。 x 和 y 将是数组的 2 个元素,由排序算法本身选择。
    • 还需要注意的是,您的块可以根据需要变得复杂或深入。只要返回值准确地反映您对 运算符的期望行为,您就可以做任何您需要的事情。因此,如果您想根据布尔值或其他东西进行排序,您可以评估该布尔值并相应地返回 -1 或 1。它派上用场了。
    • FWIW, &lt;=&gt; 被称为宇宙飞船操作员
    【解决方案2】:

    如果你有一个整数数组,比如说要排序的整数,sort 方法可以非常简单地正确排序元素 - 首先是较小的数字,最后是更大的数字。那就是你使用普通的sort,没有阻塞。

    但是当您对其他对象进行排序时,可能需要提供一种比较(每个)其中两个对象的方法。假设您有一个 Person 类的对象数组。您可能无法判断对象 bob 是否大于对象 mike(即类 Person 没有实现方法 &lt;=&gt;)。在这种情况下,您需要提供一些代码来解释您希望这些对象以何种顺序排序到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 &lt;=&gt; p2.age} 可以改写为people.sort_by{|p| p.age}
    【解决方案3】:

    @OscarRyz 的回复为我澄清了很多关于排序如何工作的问题,尤其是

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

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

    注意:获取打印块参数 e1、e2 值的参考来自 ruby-forum

    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)
    

    谢谢,

    吉格尼什

    【讨论】:

      【解决方案4】:

      &lt;=&gt; 是一个返回 (self.&lt;=&gt;( argument )) 的 ruby​​ 方法

      • -1 如果自我
      • 0 如果 self == 参数
      • 1 if self > 参数

      xy 是数组项。如果没有提供块,sort 函数使用x&lt;=&gt;y,否则块的结果表明 x 是否应该在 y 之前。

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

      这里如果 some_very_complicated_method(x, y) 返回

      【讨论】:

        【解决方案5】:

        一些杂点:

        • xy 称为块参数。 sort 方法基本上是说“我给你 x 和 y,你来决定 x 还是 y 应该放在第一位,我会处理关于排序的无聊事情”
        • &lt;=&gt; 被称为 spaceship operator

        【讨论】:

          【解决方案6】:

          在:

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

          什么是 x 和 y?

          xy 是排序算法比较的元素。

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

          对于基本数据(数字、字符串、日期等),自然顺序是预定义的,但对于客户元素(即 Employee),您可以定义在比较中谁先于谁。这个块让你有机会定义它。

          yx 会发生什么?

          在那里,他们按降序比较元素(那些具有“更高”值的将首先进行)而不是自然顺序(x&lt;=&gt;y

          &lt;=&gt; 方法代表“compareTo”,如果元素相等则返回 0,如果 xy 之前,则返回 &lt; 0,如果 x 在 @987654331 之后,则返回 &gt; 0 @

          【讨论】:

            【解决方案7】:

            我相信 |x,y| yx 一次按降序比较两个元素,如下所示: 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 代码的内部工作原理,就不可能知道 d 移动到了哪里,但我认为这个过程会一直持续到所有元素都被排序为止。 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);
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2012-04-22
              • 2013-09-06
              • 1970-01-01
              • 2016-06-25
              • 2011-05-22
              • 2018-05-25
              相关资源
              最近更新 更多