Gọi columns, rows từ một mảng 2 chiều một các đơn giản trong Ruby với Array#transpose method
Giả sử bạn có một mảng 2 chiều với 3x3 phần tử. Thông thường khi tạo mảng 2 chiều như vậy chúng ta sẽ tạo thành 1 array với 3 phần tử, mỗi phần tử lại là 1 array với 3 phần tử. [1] pry(main)> grid = [ [1] pry(main)* [1,2,3], [1] pry(main)* [4,5,6], [1] pry(main)* [7,8,9] [1] ...
Giả sử bạn có một mảng 2 chiều với 3x3 phần tử. Thông thường khi tạo mảng 2 chiều như vậy chúng ta sẽ tạo thành 1 array với 3 phần tử, mỗi phần tử lại là 1 array với 3 phần tử.
[1] pry(main)> grid = [ [1] pry(main)* [1,2,3], [1] pry(main)* [4,5,6], [1] pry(main)* [7,8,9] [1] pry(main)* ] => [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Ứng với mỗi phần tử của array gird chúng ta có một row. Viêc lấy các rows ra rất dễ dàng, nhưng lấy columns thì ngược lại, điều chúng ta mong muốn là lấy columns đơn giản như việc lấy rows. Method #transpose sinh ra giúp chúng ta làm việc đó. Để dễ hiểu chúng ta sẽ ứng dụng nó vào game đơn giản là tic-tac-toe. Chúng ta lưu bàn cờ dạng mảng 2 chiều đơn giản 3x3. Và để kiểm tra ai là người chiến thắng thì chúng ta sẽ check các hàng, cột và các đường chéo.
Truy cập columns thông qua cách thông thường
- Thông thường để truy cập trực tiếp đến các columns chúng ta sẽ sử dụng các method thông thường như map, each,.. để sinh ra các column mà không cần dùng đến mảng thứ 2. Ví dụ với grid trên ta sẽ tưởng tượng các rows, columns sẽ như sau:
Thông thường thì chúng ta sẽ lấy columns theo cách lấy từng phần tử của các rows ra. Ví dụ giá trị của cột thứ nhất:
[21] pry(main)> [grid[0][0], grid[1][0], grid[2][0]] => [1, 4, 7]
Còn đối với hàng thứ nhất, ta có thể dễ dàng lấy nó ra
[22] pry(main)> grid[0] => [1, 2, 3]
Vậy làm sao để có thể lấy các columns dễ dàng như đối với các rows?
Truy cập columns theo một cách đơn giản
Và cách đơn giản chúng ta có thể lấy các columns là sử dụng method Array#transpose mà ruby đã cung cấp cho chúng ta. Ví dụ
[3] pry(main)> columns = grid.transpose => [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
Giờ thì đơn giản rồi, chúng ta có thể lấy các columns đơn giản như đối với những rows khác
[23] pry(main)> columns[0] => [1, 4, 7]
Ứng dụng của chúng vào bài toán Tic-Tac-Toe Dưới đây không giải thích toàn bộ về thuật toán của game như thế nào, mà chúng ta chỉ dừng lại ở việc ứng dụng của method transpose vào giải quyết bài toán như thế nào thôi.
Như đã nói phần mở đầu, để chiến thắng được game thì chúng ta cần phải điền được vào 1 hàng, 1 cột hoặc 1 đường chéo. Như vậy chúng ta sẽ phải check xem các columns, các rows, hoặc các đường chéo xem các giá trị trong 1 hàng, 1 cột hoặc 1 đường chéo đó có giống nhau hay không.
Các bạn có thể xem toàn bộ code của game này tại đây và dưới đây chúng ta phân tích đoạn code họ ứng dụng thực tế như thế nào.
Dưới đây là đoạn code check rows và check columns
def check_rows @board.each { |row| return row.first if all_equal?(row) } false end def check_columns @board .transpose .each { |row| return row.first if all_equal?(row) } false end
2 functions này khác nhau duy nhất là thêm transpose để chúng ta có thể lấy các columns dễ dàng như các rows.
Chúng ta cùng xem lại một chút về function all_equal?
def all_equal?(row) return if row.first == empty row.each_cons(2).all? { |x,y| x == y } end
Đoạn code trên check 2 phần tử liên kề, nếu giống giống nhau hết thì trả về giá trị của nó. Ở trên dùng method each_cons để lấy n phần tử liên kề nhau. Ví dụ ta có mảng [1,2,3,4,5]
[2] pry(main)> a = [1,2,3,4,5] => [1, 2, 3, 4, 5] [3] pry(main)> a.each_cons(2).to_a => [[1, 2], [2, 3], [3, 4], [4, 5]] [5] pry(main)> a.each_cons(3).to_a => [[1, 2, 3], [2, 3, 4], [3, 4, 5]] [6] pry(main)> a.each_cons(4).to_a => [[1, 2, 3, 4], [2, 3, 4, 5]] [7] pry(main)> a.each_cons(5).to_a => [[1, 2, 3, 4, 5]]
Việc dùng each_cons có thể sẽ dùng cho việc so sánh là x phần tử liền kề giống nhau thì trả kết quả. Còn nếu như theo bài toàn này chúng ta đang chỉ so sánh tất cả các phần tử trong row, column hoặc hàng chéo phải bằng nhau thì chúng ta chỉ cần check size uniq row đó là được.
def all_equal?(row) return if row.first == empty row.uniq.size == 1 end
Như vậy theo đây chúng ta đã biết được thêm 2 methods mới trong array là transpose và each_cons. Hy vọng có thể giúp bạn ứng dụng vào dự án thực tế.
Reference: http://www.rubyguides.com/2017/10/ruby-transpose-method/