web-dev-qa-db-ja.com

ブロックが渡されたときにArray#sortはどのように機能しますか?

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"]
74
Ibrahim Hussein

あなたの例では

a.sort

と同等です

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

ご存知のように、配列を並べ替えるには、その要素を比較する必要があります(疑問がある場合は、比較を使用せずに並べ替えアルゴリズムを実装してみてください。<><=または>=)。

実際に提供するブロックは、2つのアイテムを比較するために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::StatFixnumStringで意味のあるところに既に提供されていることに注意してください。 、Timeなど...)。

121
bltxd

並べ替える整数の配列がある場合、sortメソッドが要素を適切に順序付けるのは非常に簡単です-最初に小さい数字、最後に大きい。これは、ブロックなしで通常のsortを使用するときです。

ただし、他のオブジェクトを並べ替える場合は、2つのオブジェクト(それぞれ)を比較する方法を提供する必要があります。クラスPersonのオブジェクトの配列があるとしましょう。オブジェクトbobがオブジェクトmikeより大きいかどうかはおそらくわかりません(つまり、クラスPersonにはメソッド<=>が実装されていません)。その場合、これらのオブジェクトをsortメソッドにソートする順序を説明するコードを提供する必要があります。それが、ブロックフォームが作動する場所です。

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

これらすべての場合、sortメソッドはそれらを同じ方法でソートします-同じアルゴリズムが使用されます。異なるのは比較ロジックです。

21

@OscarRyzの返信は、ソートがどのように機能するかについての質問で私のために多くのことをクリアしました、特に

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

私の理解に基づいて、上記のブロック結果の各比較後に配列の状態がどうなるかをここで提供しています。

注: Ruby-forumからブロックパラメーター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)

おかげで、

ジニェシュ

8
Jignesh Gohel

_<=>_は、Rubyが返すメソッドです(self.<=>( argument )

  • -1 <self <引数
  • 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)が<0のsmthを返す場合、xはyよりも小さいと見なされます...

7
Draco Ater

その他のポイント:

  • xおよびyはブロックパラメーターと呼ばれます。ソート方法は基本的に「xとyを提供します。xまたはyのどちらを先に選択するかを決定し、ソートに関して退屈なものを処理します」
  • <=>は、 宇宙船演算子 と呼ばれます。
4
Andrew Grimm

に:

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

xとyとは?

xおよびyは、ソートアルゴリズムによって比較される要素です。

これは、どの要素を他の要素の前に配置するかをカスタムクラスに定義するのに役立ちます。

基本データ(数値、文字列、日付など)については自然順序が事前定義されていますが、顧客要素(つまりEmployee)については、比較で誰が誰よりも先になるかを定義します。このブロックにより、それを定義する機会が与えられます。

そしてy <=> xで何が起こるか

そこでは、自然順序(x<=>y)ではなく、降順(「より高い」値を持つ要素が最初になる)で要素を比較しています。

<=>メソッドは「compareTo」を表し、要素が同等の場合は0を返し、xyまたは<より前の場合は> 0を返します。 xyの後にくる場合は0

3
OscarRyz

私は信じる| x、y | y <=> xは、次のように一度に2つの要素を降順で比較します。 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);
}
2
Michael F