Array # sort hoạt động như thế nào khi một khối được chuyển qua?


76

Tôi đang gặp vấn đề trong việc hiểu cách thức array.sort{ |x,y| block }hoạt động chính xác, do đó làm thế nào để sử dụng nó?

Một ví dụ từ tài liệu 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"]

Câu trả lời:


125

Trong ví dụ của bạn

a.sort

tương đương với

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

Như bạn đã biết, để sắp xếp một mảng, bạn cần để có thể so sánh các yếu tố của nó (nếu bạn nghi ngờ rằng, chỉ cần cố gắng thực hiện bất kỳ thuật toán sắp xếp mà không sử dụng bất cứ so sánh, không có <, >, <=hoặc >=).

Khối bạn cung cấp thực sự là một hàm sẽ được sortthuật toán gọi để so sánh hai mục. Đó là xysẽ luôn là một số phần tử của mảng đầu vào được sortthuật toán chọn trong quá trình thực thi nó.

Các sortthuật toán sẽ cho rằng sự so sánh này hàm / block sẽ đáp ứng các yêu cầu về phương pháp <=>:

  • trả về -1 nếu x <y
  • trả về 0 nếu x = y
  • trả về 1 nếu x> y

Việc không cung cấp một hàm / khối so sánh đầy đủ sẽ dẫn đến mảng có thứ tự không được xác định.

Bây giờ bạn sẽ hiểu tại sao

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

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

trả về cùng một mảng theo thứ tự ngược lại.


Để giải thích rõ hơn về những gì Tate Johnson đã thêm, nếu bạn triển khai hàm so sánh <=>trên bất kỳ lớp nào của mình, bạn sẽ đạt được những điều sau

  1. Bạn có thể bao gồm các module Comparabletrong lớp học của bạn sẽ tự động xác định cho bạn những phương pháp sau đây: between?, ==, >=, <, <=>.
  2. Các thể hiện của lớp của bạn bây giờ có thể được sắp xếp bằng cách sử dụng lệnh gọi mặc định (tức là không có đối số) tới sort.

Lưu ý rằng các <=>phương pháp đã được cung cấp bất cứ nơi nào nó có ý nghĩa trong thư viện chuẩn của ruby ( Bignum, Array, File::Stat, Fixnum, String, Time, vv ...).


1
Tôi nghĩ rằng nó đáng để thêm vào đó <=>là một phương thức được xác định trong Comparablevà trộn với String. FixnumBignumcũng xác định <=>. Bạn có thể triển khai <=>trong bất kỳ lớp nào mà việc sắp xếp hoặc so sánh là cần thiết.
Tate Johnson

Tôi đã đánh dấu câu quan trọng. x và y sẽ là 2 phần tử của mảng của bạn, được chọn bởi chính thuật toán sắp xếp.
bltxd

1
Cũng cần lưu ý rằng khối của bạn có thể phức tạp hoặc có chiều sâu nếu cần. Miễn là giá trị trả về phản ánh chính xác hành vi bạn mong đợi từ toán tử <=> của mình, bạn có thể làm bất cứ điều gì bạn cần. Vì vậy, nếu bạn muốn sắp xếp dựa trên boolean hoặc thứ gì đó, bạn có thể đánh giá boolean đó và trả về -1 hoặc 1 tương ứng. Nó có ích.
Matthew

FWIW, <=>được gọi là nhà điều hành Tàu vũ trụ
B Seven

22

Khi bạn có một mảng, giả sử, các số nguyên để sắp xếp, sẽ khá đơn giản đối với sortphương pháp sắp xếp thứ tự các phần tử một cách hợp lý - số nhỏ hơn trước, lớn hơn ở cuối. Đó là khi bạn sử dụng thông thường sort, không có khối.

Nhưng khi bạn đang phân loại các đối tượng khác, có thể cần cung cấp một cách để so sánh (từng) hai trong số chúng. Giả sử bạn có một mảng các đối tượng của lớp Person. Bạn có thể không biết đối tượng bobcó lớn hơn đối tượng hay không mike(nghĩa là lớp Personkhông có phương thức được <=>triển khai). Trong trường hợp đó, bạn cần cung cấp một số mã để giải thích thứ tự bạn muốn các đối tượng này được sắp xếp theo sortphương thức nào. Đó là nơi mà hình thức khối bắt đầu.

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

vv Trong tất cả các trường hợp này, sortphương pháp sắp xếp chúng theo cùng một cách - cùng một thuật toán được sử dụng. Điều khác biệt là logic so sánh.


Trung thực nhận thấy câu trả lời này hữu ích hơn nhiều. Một bức tranh nói một nghìn từ và một ví dụ nói một nghìn dòng giải thích.
Kevin Monk

Lưu ý: people.sort{|p1,p2| p1.age <=> p2.age}có thể được viết lại thànhpeople.sort_by{|p| p.age}
Cyoce

9

Câu trả lời của @OscarRyz đã giải đáp rất nhiều cho tôi về câu hỏi về cách thức hoạt động của phân loại, đặc biệt

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

Dựa trên hiểu biết của tôi, tôi sẽ cung cấp ở đây trạng thái của mảng sẽ như thế nào sau mỗi lần so sánh cho các kết quả khối ở trên.

Lưu ý: Có tham chiếu về việc in các giá trị của các tham số khối e1, e2 từ 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"]

Trạng thái mảng được đoán trong thời gian chạy sau mỗi lần so sánh:

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

Cảm ơn,

Jignesh


7

<=>là một phương thức là ruby ​​trả về ( self.<=>( argument ))

  • -1 nếu tự <đối số
  • 0 nếu tự luận ==
  • 1 nếu tự> đối số

xylà các mục của mảng. Nếu không có khối nào được cung cấp, sorthàm sẽ sử dụng x<=>y, nếu không, kết quả của khối cho biết nếu x phải trước y.

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

Ở đây nếu some_very_complicated_method (x, y) trả về smth <0, x được coi là <so với y, v.v.


4

Một số điểm khác:

  • xyđược gọi là tham số khối. Về cơ bản, phương pháp sắp xếp nói rằng "Tôi sẽ cho bạn x và y, bạn xác định xem x hay y nên đến trước và tôi sẽ xem xét những thứ nhàm chán liên quan đến sắp xếp"
  • <=>được gọi là toán tử tàu vũ trụ .

3

Trong:

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

x và y là gì?

xylà các phần tử đang được so sánh bằng thuật toán sắp xếp.

Điều này rất hữu ích để xác định cho các lớp tùy chỉnh phần tử nào phải đứng trước phần tử kia.

Đối với dữ liệu cơ bản (số, chuỗi, ngày, v.v.), thứ tự tự nhiên được xác định trước, nhưng đối với phần tử khách hàng (tức là Nhân viên), bạn xác định ai đi trước ai trong một so sánh. Khối này cho bạn cơ hội xác định điều đó.

và điều gì xảy ra tại y <=> x?

Ở đó, họ đang so sánh các phần tử theo thứ tự giảm dần (những phần tử có giá trị "cao hơn" sẽ đi trước) thay vì thứ tự tự nhiên ( x<=>y)

Các <=>phương pháp viết tắt của "compareTo" và trở về 0 nếu các yếu tố tương đương, hoặc <0 nếu xđi trước hơn yhoặc >0 nếu xđi sauy


2

Tôi tin rằng | x, y | y <=> x đang so sánh hai phần tử tại một thời điểm theo thứ tự giảm dần, như được thấy trong: http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-3C-3D- 3E Nói với ["d", "a", "e", "c", "b"], "d" và "a" dường như được so sánh trước. Sau đó, vì nó giảm dần, cả hai vẫn theo cùng một thứ tự vì d ước lượng nhỏ hơn a. Sau đó d và e được đánh giá. "e" được chuyển đến vị trí của "d". Nếu không biết hoạt động bên trong của mã c thì không thể biết d được chuyển đến đâu nhưng tôi nghĩ quá trình này tiếp tục cho đến khi tất cả các phần tử được sắp xếp. Các chức năng 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);
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.