Як працює сортування масиву # при передачі блоку?


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

Відповіді:


125

У вашому прикладі

a.sort

еквівалентно

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

Як ви знаєте, для сортування масиву, ви повинні бути в змозі порівняти його елементи (якщо ви сумніваєтеся, спробуйте реалізувати будь-який алгоритм сортування без використання будь - які порівняння, немає <, >, <=або >=).

Блок, який ви надаєте, насправді є функцією, яка буде викликана sortалгоритмом для порівняння двох елементів. Тобто xі yзавжди будуть деякі елементи вхідного масиву, обрані sortалгоритмом під час його виконання.

sortАлгоритм буде вважати , що це порівняння функції / блок буде відповідати вимогам , що пред'являються до методу <=>:

  • повернути -1, якщо x <y
  • поверніть 0, якщо x = y
  • поверніть 1, якщо x> y

Якщо не надати адекватну функцію порівняння / блок, це призведе до масиву, порядок якого невизначений.

Тепер ви повинні зрозуміти, чому

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

і

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

повернути той самий масив у протилежних порядках.


Щоб детальніше розповісти про те, що додав Тейт Джонсон, якщо ви застосуєте функцію порівняння <=>на будь-якому з ваших класів, ви отримаєте наступне

  1. Ви можете включати модуль Comparableв своєму класі , який буде автоматично визначати для вас такі методи: between?, ==, >=, <, <=і >.
  2. Екземпляри вашого класу тепер можна сортувати за допомогою виклику за замовчуванням (тобто без аргументу) sort.

Зверніть увагу , що <=>метод вже надається всюди , де це має сенс в стандартній бібліотеці рубіна ( Bignum, Array, File::Stat, Fixnum, String, Timeі т.д. ...).


1
Я думаю, варто додати, що <=>це метод, визначений Comparableі змішаний з String. Fixnumа Bignumтакож визначити <=>. Ви можете впровадити <=>в будь-якому класі, де необхідне сортування або порівняння.
Тейт Джонсон,

Я виділив важливе речення. x та y будуть 2 елементами вашого масиву, вибраними самим алгоритмом сортування.
bltxd

1
Також добре зауважити, що ваш блок може бути настільки складним або глибоким, наскільки це необхідно. Поки повернене значення точно відображає поведінку, яку ви очікуєте від вашого оператора <=>, ви можете робити все, що вам потрібно. Отже, якщо ви хочете відсортувати на основі логічного значення або чогось іншого, ви можете оцінити це логічне значення і повернути -1 або 1 відповідно. Це стане в нагоді.
Matthew

FWIW, <=>відомий як оператор космічного корабля
B Сім

22

Коли у вас є масив, скажімо, цілих чисел для сортування, це досить просто для sortметоду, щоб правильно впорядкувати елементи - спочатку менші числа, більші в кінці. Ось тоді ви використовуєте звичайний sort, без блоку.

Але коли ви сортуєте інші об’єкти, може знадобитися спосіб порівняння (кожного) двох з них. Скажімо, у вас є масив об’єктів класу 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

9

Відповідь @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)

Дякую,

Джигнеш


7

<=>це метод ruby, який повертає ( self.<=>( argument ))

  • -1 якщо аргумент self <
  • 0, якщо аргумент self ==
  • 1 якщо аргумент self>

xі yє елементами масиву. Якщо жодного блоку не передбачено, 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 є елементами, що порівнюються алгоритмом сортування.

Це корисно визначити для користувацьких класів, який елемент повинен бути перед іншим.

Для основних даних (цифр, рядків, дати тощо) природний порядок визначений заздалегідь, але для елемента замовника (тобто працівника) ви визначаєте, хто передує перед ким у порівнянні. Цей блок дає вам можливість це визначити.

і що відбувається при y <=> x?

Там вони порівнюють елементи за спаданням (ті, що мають "вище" значення, йдуть першими), а не за природним порядком ( x<=>y)

<=>Метод означає «CompareTo» і повертає 0 , якщо елементи еквівалентні, або <0 , якщо xйде перед чим yабо >0 , якщо xйде після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, неможливо дізнатись, куди переміщено 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);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.