Ruby blocks!
Bài đăng này đã không được cập nhật trong 3 năm
Blocks là một tính năng có vai trò quan trọng của ruby. Bài viết sau sẽ chia sẻ về cách blocks hoạt động ra sao và những hữu ích mà nó mang lại.
Blocks trong ruby là gì?
Một block bạn thường thấy là đoạn code được đặt trong do
và end
. Bạn có thể viết block bằng 2 cách: nhiều dòng code đặt trong cấu trúc câu do
-end
, và trên một dòng giữa {
-}
. Cả hai cách viết đều cho kết quả giống nhau và nên sử dụng do-end
nếu code của bạn có nhiều hơn một dòng, nó sẽ giúp cho dễ đọc hơn.
Ví dụ đơn giản cho một block viết theo multi-line như sau:
[1, 2, 3].each do |n|
puts "Number #{n}"
end
hoặc có thể viết thành inline như sau
[1, 2, 3].each {|n| puts "Number #{n}"}
Cả 2 cách đều in ra các số 1, 2, 3 theo thứ tự. Ký tự n
được gọi là tham số của block và trong trường hợp này là nó mang giá trị của một số trong mỗi lần lặp, theo thứ tự của mảng. Do đó trong lần lặp đầu, n
có giá trị là 1, sau đó lần lặp sau giá trị là 2, là 3 tương ứng.
Kết quả in ra
Number 1
Number 2
Number 3
=> [1, 2, 3]
yield trong block có chức năng gì
Nếu là lần đầu sử dụng, yield có thể gây nhầm lẫn về cách mà nó gọi block và truyền tham số cho nó. Ví dụ như sau
def my_method
puts "reached the top"
yield
puts "reached the bottom"
end
my_method do
puts "reached yield"
end
Kết quả là
reached the top
reached yield
reached the bottom
=> nil
Về cơ bản khi thực thi my_method
, khi tới lời gọi yield
, code bên trong block được thực thi, khi đoạn code trong block kết thúc, hàm my_method
tiếp tục thực thi.
Truyền blocks cho method
Một method không cần phải chỉ định là có phải truyền block hay không. Bạn có thể truyền một block hoặc function bất kỳ nhưng nếu method đó không gọi yield
, block đó sẽ không được thực thi.
Ngược lại nếu trong method có khai báo yield
, block lại trở thành bắt buộc mỗi khi gọi method và nếu không có, method sẽ đưa ra một ngoại lệ rằng nó không nhận được block nào.
Nếu bạn muốn khiến block trở thành một tùy chọn, bạn có thể sử dụng phương thức block_given?
, nó sẽ trả về có hay không dựa trên lời gọi method có được truyền block hay không.
Truyền tham số cho yield
Bất kể tham số nào đưa vào yield
sẽ được coi như một tham số của block. Do đó khi block được chạy, nó có thể sử dụng tham số được truyền vào từ lời gọi method, các tham số đó sẽ được coi như các biến cục bộ trong trong yield
Thứ tự sắp xếp của tham số là quan trọng bởi thứ tự bạn đưa vào là thứ tự mà block nhận đúng được
Một điều đáng lưu ý ở đây rằng tham số trong block là cục bộ trong block và chỉ sử dụng được trong block thôi.
Tham số &block có ý nghĩa gì
Bạn có thể nhìn thấy &block
ở rất nhiều nơi trong ruby. Nó là cách bạn tham chiếu tới một block thay vì biến cục bộ vào một method. Ruby cho phép bạn truyền vào bất kỳ một đối tượng cho một method ngay cả khi đó là một block. Method sẽ cố sử dụng đối tượng truyền vào như một block và nếu nó không phải một block, method sẽ gọi to_proc
để chuyển dổi nó thành một block.
Lưu ý rằng block
chỉ là một cái tên cho tham chiếu tới block, bạn có thể sử dụng tên bất kỳ thay vì block
để làm nó có ý nghĩa gợi tả hơn.
def my_method(&block)
puts block
block.call
end
my_method { puts "Hello!" }
Kết quả là
#<Proc:0x0000010124e5a8@tmp/example.rb:6>
Hello!
Như bạn có thể thấy ví dụ trên, biến block
bên trong my_method
làm một tham chiếu tới block và nó có thể được thực thi với lời gọi call
. call
trong block là cách sử dụng khác của yield
, một vài người thích sử dụng block.call hơn thay vì yield
với lý do dễ đọc hơn.
Trả về giá trị
yield
trả về giá trị tính toán của biểu thức cuối cùng, do đó có thể nói rằng giá trị yield
trả về là giá trị block trả về.
def my_method
value = yield
puts "value is: #{value}"
end
my_method do
2
end
Kết quả là
value is 2
=> nil
Hoạt động của .map(&:something) như thế nào
Bạn có thể sử dụng cách viết .map(&:capitalize)
rất nhiều. Đó là cách viết khác của .map{|title| title.capitalize}
Bạn có thể tự hỏi là nó làm việc như thế nào, đó chính là sysbol class được dành riêng và thi hành to_proc
để lấy ra phương thức tương ứng với symbol đó.
Kết luận
Bạn có thể nghĩ block là một đoạn code, và yield
cho phép bạn đưa vào một đoạn code khác vào một nơi trong method. Điều đó có nghĩa bạn có thể có một method làm việc theo nhiều cách khác nhau và bạn không cần phải viết nhiều method.
Refs
All rights reserved