Khác biệt giữa Block, Proc và Lambda trong Ruby
Block, proc và lambda là một trong những đặc điểm rất hay của Ruby. Nói đơn giản thì nó là một cách để tập hợp một hay nhiều dòng code được đặt trong 2 dấu ngoặc nhọn { } hoặc do...end. Convention chung là nếu chỉ có 1 dòng code thì đặt trong { }, còn nhiều hơn 2 dòng thì ta đặt nó trong do...end. ...
Block, proc và lambda là một trong những đặc điểm rất hay của Ruby. Nói đơn giản thì nó là một cách để tập hợp một hay nhiều dòng code được đặt trong 2 dấu ngoặc nhọn { } hoặc do...end. Convention chung là nếu chỉ có 1 dòng code thì đặt trong { }, còn nhiều hơn 2 dòng thì ta đặt nó trong do...end. Chúng thường được kết hợp với những methods như each, map để thực thi một việc nào đó cho từng element trong một hash hoặc array. Ví dụ:
- Block
[1, 2, 3].each {|x| puts x} [1, 2, 3].each do |x| x += 6 puts x end
- Proc
p = Proc.new {|x| puts x} # tạo 1 đối tượng Proc [1, 2, 3].each(&p) # dấu & giúp chuyển p thành block để truyền vào each p = Proc.new {puts "Dota 2"} p.call # thực thi đoạn code nằm trong body của p
- Lambda
l = lambda {|x| puts x} [1, 2, 3].each(&l) l = lambda {puts "Dota 2"} l.call
Từ những ví dụ trên ta có thể thấy block, proc và lambda về cơ bản rất giống nhau, tuy nhiên giữa chúng vẫn có 1 số khác biệt cần lưu ý.
Giữa block và proc
Proc là object còn block thì không p = Proc.new {puts "Dota 2"} thì p là một instance của class Proc, chúng ta có thể gọi method trên nó và cũng có thể gán nó vào 1 biến. Ví dụ:
p.call => "Dota 2" p.class => "Proc"
Ngược lại với Proc, block không phải là object, nó chỉ là 1 đoạn code được đặt trong { } hoặc do...end thôi, bản thân nó vốn không có nghĩa gì cả, và nó chỉ có thể được truyền như một đối số. Ví dụ:
{puts "Dota 2"} => lỗi cú pháp a = {puts "Dota 2"} => lỗi cú pháp [1, 2, 3].each {|x| print x} => 123
Giữa Proc và Lambda
Proc và Lambda rất giống nhau, bởi vì chúng đều là Proc object.
p = Proc.new {puts "Dota 2"} l = lambda {puts "Dota 2"} p.class => Proc l.class => Proc
Giữa chúng chỉ có 2 sự khác biệt chính như sau:
- Lambda kiểm tra số đối số truyền vào trong khi proc thì không. Có nghĩa là lambda sẽ throw error nếu số đối số truyền vào nó không đúng còn proc sẽ bỏ qua những đối số dư và gán nil cho những đối số bị thiếu
p = Proc.new {|x| puts x} p.call("Dota 2") => "Dota 2" p.call => nil p.call("Dota 2", "Hello") => "Dota 2" l = lambda {|x| puts x} l.call("Dota 2") => "Dota 2" l.call => ArgumentError: wrong number of arguments (0 for 1) l.call("Dota 2", "Hello") => ArgumentError: wrong number of arguments (2 for 1)
- Giả sử proc và lambda được gọi trong một method nào đó, return ở trong lambda sẽ pass control về cho method gọi nó, còn proc thì sẽ return ngay lập tức mà không quay lại method đó Ví dụ:
def test_lambda l = lambda {return} l.call puts "Dota 2" end test_lambda => return sẽ quay về method test_lambda và tiếp tục thực thi phía dưới, kết quả ra "Dota 2" def test_proc p = Proc.new{return} p.call puts "Dota 2" end test_proc => return sẽ lập tức ra khỏi method test_proc, kết quả là không có gì cả
- Proc, lambda là object, block thì không
- Lambda kiểm tra số đối số, proc thì không
- Lambda và proc khi gặp return có cách xử lí khác nhau