Blocks, Procs và Enumerable trong Ruby
Bài đăng này đã không được cập nhật trong 7 năm
Enumerable
mà một module rất hay có ở trong Ruby. Nó cung cấp cho chúng ta rất nhiều hàm hữu ích như each
, map
, inject
, ... Các hàm nói trên rõ ràng, dễ đọc và dễ hiểu hơn for
ở những ngôn ngữ khác.
Enumerable
được kết hợp với một trong những cấu trúc rất hay khác của Ruby là blocks
.
Ví dụ:
collection.each { |item| puts item }
hoặc
collection.map do |item|
item * 2
end
Một cách ngắn gọn hơn có thể bạn đã biết :
collection.all?(&:valid?)
hoặc
collection.inject(:&)
Và với bài viết này chúng ta hãy tìm hiểu rõ hơn về nó.
Blocks
Blocks
cho phép các method lấy các đoạn mã tùy (function) ý như một đối số và thực thi chúng. Việc truyền 1 function dạng anonymous (hàm vô danh, tự chạy ko cần gọi) vào trong 1 hàm được sử dụng phổ biến ở nhiều ngôn ngữ. Js là 1 ví dụ điển hình của việc sử dụng callback mà có hàm lồng trong hàm
item.action(function(result) {
result.otherAction(function(newResult) {
console.log(newResult);
});
});
Trong Ruby, bạn có biểu diễn logic "nhận mỗi phần tử của một mảng với 2 rồi trả ra kết quả" cùng cách sử dụng map
của Enumerable
module:
[1, 2, 3].map { |n| n * 2 }
# => [2, 4, 6]
Procs
Khái niệm truyền một function như một đối số vào các function khác rất hay và ứng dụng rất mạnh mẽ. Các method có khả năng này được gọi là hàm bậc cao. Ruby cho phép thực hiện điều đó với Procs
.
Procs
là một function object của Ruby. Chúng ta có thể truyền một proc thay vì block tới bất kỳ method nào bằng cách sử dụng tiền tố &
. Ví dụ :
double = Proc.new { |n| n * 2 }
[1, 2, 3].map(&double)
# => [2, 4, 6]
to_proc
Trong trường hợp bạn không thể ngay lặp tức chạy được Proc để truyền function Rails cung cấp cho chúng ta một trick rất hay là to_proc
được gọi trước khi thực thi.
class Doubler
def double(n)
n * 2
end
def to_proc
method(:double).to_proc
end
end
doubler = Doubler.new
[1, 2, 3].map(&doubler)
# => [2, 4, 6]
Symbols
Symbols
khai báo một to_proc
như là một function trùng tên dưới dạng symbol và truyền vào object
ví dụ
[1,2,3].map(&:to_s) # => ["1", "2", "3"]
Ở ví dụ trên thì mỗi phần tử của array sẽ được to_s
Symbols
giúp chúng ta dễ viết test hơn, code dễ hiểu hơn.
Ví dụ “are all the numbers even?” có thể được thể hiện là
numbers.all?(&:even?)
thay vì
numbers.all? { |n| n.even? }
inject
inject
hay reduce
là một trong những method mạnh mẽ nhất được Enumerable
cung cấp. Trên thực tế, mội method khác của Enumerable
đều có thể viết lại dưới dạng inject
. Tuy nhiên inject
có một chút khác biệt. Nó cũng sử dụng block như các method khác nhưng chấp nhận một symbol thay cho một block và symbol đó sẽ được sử dụng để truyền tới object giống như to_proc
[1, 2, 3].inject(&:+) # => 6
hoặc
[1, 2, 3].inject(:+) # => 6
All rights reserved