Wie funktioniert die Array # -Sortierung, wenn ein Block übergeben wird?

76

Ich habe ein Problem damit, zu verstehen, wie es array.sort{ |x,y| block }genau funktioniert und wie ich es verwende.

Ein Beispiel aus der Ruby-Dokumentation :

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

Antworten:

125

In deinem Beispiel

a.sort

ist äquivalent zu

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

Wie Sie wissen, um ein Array zu sortieren, müssen Sie in der Lage sein , seine Elemente zu vergleichen (wenn man das bezweifeln, versuchen Sie einfach , ohne jeden Vergleich jede Art Algorithmus zu implementieren, nicht <, >, <=oder >=).

Der von Ihnen bereitgestellte Block ist wirklich eine Funktion, die vom sortAlgorithmus aufgerufen wird , um zwei Elemente zu vergleichen. Das sind xund ybleiben einige Elemente des Eingabearrays, das der sortAlgorithmus während seiner Ausführung ausgewählt hat.

Der sortAlgorithmus geht davon aus, dass diese Vergleichsfunktion / dieser Vergleichsblock die Anforderungen für die Methode erfüllt <=>:

  • Rückgabe -1, wenn x <y
  • 0 zurückgeben, wenn x = y
  • Geben Sie 1 zurück, wenn x> y

Wenn keine angemessene Vergleichsfunktion / kein adäquater Vergleichsblock bereitgestellt wird, führt dies zu einem Array, dessen Reihenfolge undefiniert ist.

Sie sollten jetzt verstehen, warum

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

und

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

Geben Sie dasselbe Array in entgegengesetzter Reihenfolge zurück.


Wenn Sie die Vergleichsfunktion <=>für eine Ihrer Klassen implementieren , erhalten Sie Folgendes, um zu erläutern, was Tate Johnson hinzugefügt hat

  1. Sie können das Modul enthalten Comparablein der Klasse , die für Sie die folgenden Methoden automatisch definieren: between?, ==, >=, <, <=und >.
  2. Instanzen Ihrer Klasse können jetzt mit dem Standardaufruf (dh ohne Argument) sortiert werden sort.

Beachten Sie, dass das <=>Verfahren vorgesehen ist , bereits überall dort , wo es sinnvoll , in Rubys Standardbibliothek macht ( Bignum, Array, File::Stat, Fixnum, String, Time, etc ...).

bltxd
quelle
1
Ich denke, es lohnt sich hinzuzufügen, dass dies <=>eine Methode ist, die in definiert Comparableund gemischt wird String. Fixnumund Bignumauch definieren <=>. Sie können <=>in jeder Klasse implementieren, in der Sortierung oder Vergleiche erforderlich sind.
Tate Johnson
Ich habe den wichtigen Satz hervorgehoben. x und y sind zwei Elemente Ihres Arrays, die vom Sortieralgorithmus selbst ausgewählt werden.
Bltxd
1
Es ist auch gut zu beachten, dass Ihr Block so kompliziert oder tiefgreifend wie nötig sein kann. Solange der Rückgabewert genau das Verhalten widerspiegelt, das Sie von Ihrem <=> -Operator erwarten, können Sie alles tun, was Sie benötigen. Wenn Sie also nach einem Booleschen Wert oder etwas anderem sortieren möchten, können Sie diesen Booleschen Wert auswerten und -1 oder 1 entsprechend zurückgeben. Es ist praktisch.
Matthew
FWIW, <=>ist bekannt als der Raumschiff-Betreiber
B Seven
22

Wenn Sie ein Array von beispielsweise zu sortierenden Ganzzahlen haben, ist es für die sortMethode ziemlich einfach , die Elemente richtig zu ordnen - kleinere Zahlen zuerst, größere am Ende. Das ist, wenn Sie gewöhnliche sort, ohne Block verwenden.

Wenn Sie jedoch andere Objekte sortieren, muss möglicherweise eine Möglichkeit bereitgestellt werden, (jeweils) zwei davon zu vergleichen. Angenommen, Sie haben eine Reihe von Klassenobjekten Person. Sie können wahrscheinlich nicht erkennen, ob das Objekt bobgrößer als das Objekt ist mike(dh in der Klasse Personist keine Methode <=>implementiert). In diesem Fall müssen Sie Code bereitstellen, um zu erklären, in welcher Reihenfolge diese Objekte nach sortMethode sortiert werden sollen. Hier setzt die Blockform an.

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

usw. In all diesen Fällen sortsortiert die Methode sie auf die gleiche Weise - es wird der gleiche Algorithmus verwendet. Was anders ist, ist die Vergleichslogik.

Mladen Jablanović
quelle
Fand diese Antwort viel hilfreicher, um ehrlich zu sein. Ein Bild sagt mehr als tausend Worte und ein Beispiel sagt mehr als tausend Erklärungen.
Kevin Monk
Hinweis: people.sort{|p1,p2| p1.age <=> p2.age}könnte umgeschrieben werden alspeople.sort_by{|p| p.age}
Cyoce
9

Die Antwort von @OscarRyz hat mir die Frage, wie die Sortierung funktioniert, sehr klar gemacht

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

Nach meinem Verständnis gebe ich hier an, wie der Zustand des Arrays nach jedem Vergleich für die obigen Blockergebnisse aussehen würde.

Hinweis: Die Referenz zum Drucken der Werte der Blockparameter e1, e2 wurde vom Ruby-Forum abgerufen

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"]

Ein erratener Array-Status zur Laufzeit nach jedem Vergleich:

 [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)

Vielen Dank,

Jignesh

Jignesh Gohel
quelle
7

<=>ist eine Methode ist Ruby, die zurückgibt ( self.<=>( argument ))

  • -1 wenn self <Argument
  • 0 wenn self == Argument
  • 1 wenn Selbst> Argument

xund ysind Elemente des Arrays. Wenn kein Block bereitgestellt wird, verwendet die sortFunktion x<=>y, andernfalls sagt das Ergebnis des Blocks, ob x vor y stehen soll.

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

Wenn some_very_complicated_method (x, y) smth zurückgibt, das <0 ist, wird x als <als y betrachtet und so weiter ...

Draco Ater
quelle
4

Einige verschiedene Punkte:

  • xund ywerden Blockparameter genannt. Die Sortiermethode sagt im Grunde: "Ich gebe Ihnen x und y, Sie bestimmen, ob x oder y zuerst kommen sollen, und ich kümmere mich um das langweilige Zeug in Bezug auf das Sortieren."
  • <=>wird als Raumschiffbetreiber bezeichnet .
Andrew Grimm
quelle
3

Im:

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

Was ist x und y?

xund ysind die Elemente, die vom Sortieralgorithmus verglichen werden.

Dies ist nützlich, um für benutzerdefinierte Klassen zu definieren, welches Element vor dem anderen stehen soll.

Für Basisdaten (Zahlen, Zeichenfolgen, Datum usw.) ist die natürliche Reihenfolge vordefiniert, aber für Kundenelemente (dh Mitarbeiter) definieren Sie in einem Vergleich, wer vor wem geht. Dieser Block gibt Ihnen die Möglichkeit, dies zu definieren.

und was passiert bei y <=> x?

Dort vergleichen sie die Elemente in absteigender Reihenfolge (diejenigen mit "höherem" Wert werden zuerst verwendet) und nicht in der natürlichen Reihenfolge ( x<=>y)

Die <=>Methode steht für "compareTo" und gibt 0 zurück, wenn die Elemente äquivalent sind, oder <0, wenn xvorher geht, yoder >0, wenn xgeht nachhery

OscarRyz
quelle
2

Ich glaube | x, y | y <=> x vergleicht zwei Elemente gleichzeitig in absteigender Reihenfolge, wie in: http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-3C-3D- 3E Sagen Sie mit ["d", "a", "e", "c", "b"], "d" und "a" scheinen zuerst verglichen zu werden. Da es dann absteigend ist, bleiben beide in derselben Reihenfolge, da d weniger als a ergibt. Dann werden d und e ausgewertet. "e" wird in die Position von "d" verschoben. Ohne die interne Funktionsweise des c-Codes zu kennen, ist es nicht möglich zu wissen, wohin d verschoben wird, aber ich denke, dieser Prozess wird fortgesetzt, bis alle Elemente sortiert sind. Die c-Funktionen:

           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);
}
Michael F.
quelle