12/08/2018, 16:56

Ruby blocks!

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 ...

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. example

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 demo 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

Mastering ruby blocks

0