12/08/2018, 14:10

Phân biệt Block, Proc, lambda trong Ruby

Những người mới băt đầu tìm hiểu về ruby, đặc biệt là ruby on rails thường rất khó khăn trong việc phân biệt Block, Proc và Lambda. Các tính năng này theo mình nghĩ là một trong các tính năng mạnh nhất của Ruby, nhưng cũng giống mọi người mới học, sự phân biệt rạch ròi khái niệm giữa ...

  • Những người mới băt đầu tìm hiểu về ruby, đặc biệt là ruby on rails thường rất khó khăn trong việc phân biệt Block, Proc và Lambda.

  • Các tính năng này theo mình nghĩ là một trong các tính năng mạnh nhất của Ruby, nhưng cũng giống mọi người mới học, sự phân biệt rạch ròi khái niệm giữa chúng cũng khá là khó khăn. Bài viết này của mình sẽ cung cấp 1 số ví dụ đơn giản nhất để bạn có thể phân biệt các chức năng này và sử dụng nó như thế nào cho hợp lý.

  • Đầu tiên, khái niệm chung nhất giữa 3 tính năng này đó chính là đều cho phép bạn thực hiện một khối lệnh được định nghĩa ở trong 1 method nào đó và gọi nó lúc cần dùng đến. Đó là khái niệm chung, vậy khái niệm của riêng từng tính năng thì sao?

1. Block??

  • Block được sử dụng rất nhiều trong ruby và định nghĩa của nó cũng đơn giản chỉ là tập hợp các lệnh thành 1 khối duy nhất. Các khối lệnh này được đặt trong {...} hoặc do ...end

  • Thông thường block được dùng để thực hiện một chức năng nào đó đối với từng phần tử trong một array hoặc hash.

VD:

[1, 2, 3, 4].each {|x| puts x }
  • Sử dụng block có một hạn chế, trong ví dụ trên, nếu bạn có một array khác và cũng muốn hiển thị giá trị của array đó thì bạn phải viết lại toàn bộ block, sẽ là cực hình nếu block dài.

2. Proc??

  • Block là cú pháp dùng một lần, vậy nếu muốn sử dụng nhiều lần 1 block thì sao? Ý tưởng được nghĩ đến đầu tiên đó là đặt tên cho block đó và gọi nó mỗi khi ta cần dùng, và đó cũng chính là Proc
  • Để dễ hiểu, chúng ta hãy xem thử cách tạo 1 proc với khối lệnh giống trong ví dụ ở phần trên nhé.
p = Proc.new {|x| puts x}
  • Ở đây, ta định nghĩa p chính là tên của block cần sử dụng và có thể dễ dàng gọi nó khi cần như trong ví dụ dưới đây:
[1, 2, 3, 4].each(&p)
[“handsome”, “intelligent”, “manly”].each(&p)
  • Ký hiệu & để hiểu tham số truyền vào là 1proc, bản chất nó là chuyển symbol thành proc object, và truyền vào cho hàm như 1 block. Và proc dễ thấy chính là 1 object.

3. Lambda

  • Lambda nói về chức năng thì khá giống với 1 proc tuy nhiên nó cũng có vài sự khác biệt đáng được kể đến.

  • Cách khởi tạo 1 lambda thì khá dễ dàng ví dụ:

lambda = lambda { |x| x + 1 }
  • Giống như proc, lambda là 1 object.

Vậy sự khác nhau giữa chúng là gì??

Vì Proc và Lambda khá giống nhau nên mình sẽ đưa ra sự khác nhau giữa Block và Proc rồi sau đó là Proc và Lambda.

  • Nào, bắt đầu với Block và Proc

    • Từ định nghĩa, dễ dàng thấy được rằng proc là 1 object còn block là không, chính vì vậy proc có thể dễ dàng gọi lại vì nó có định danh còn block thì không có khả năng này
    • Tiếp đến, Proc khi đứng một mình vẫn được định nghĩa còn Block thì không, nó chỉ là 1 thành phần trong 1 câu lệnh, nếu đứng không sẽ không có ý nghĩa gì cả
    • Ngoài ra, khi sử dụng trong một hàm thì với block chỉ được dùng 1 còn Proc thì khác, chúng ta có thể gọi nhiều proc trong danh sách đối số của hàm đó. Ví dụ như:
 def call_procs(p1, p2)
    p1.call
    p2.call
end
a = Proc.new { puts "First proc" }
b = Proc.new { puts "Second proc" }
call_procs(a,b)
  • Giờ đến với sự khác biệt giữa Proc và Lambda

    • Trước hết, theo mình hiểu thì Lambda chính là thuộc Class Proc. Sự khác biệt của Proc và Lambda với mình cũng còn khá mơ hồ nên mình xin phép đưa ra 2 sự khác biệt cơ bản mà mình tìm hiểu.
    • Thứ nhất, Lambda thì check số lượng tham số truyền vào, còn Proc thì không check điều này
p = Proc.new { |x| puts x }
p.call(1, 2)
# return 1
l = lambda { |x| puts x }
l.call(1, 2)
# return Argument Error
    • Cách xử lý từ khóa return là khác nhau giữa proc và lambda, cụ thể với return trong proc, hàm gọi bên ngoài cũng sẽ kết thúc ngay lập tức, trong khi đó lambda vẫn sẽ tiếp tục thưc hiện các câu lệnh tiếp theo của hàm gọi cho đến khi dừng hẳn.
def test
  l = lambda {return }
  l.call
  puts “This line can display”
end
test
# => This line can display
def test1
  p = Proc.new { return }
  p.call
  puts “This line can’t display”
end
test1
# => nil

Trên đây là một số tìm hiểu và cách phân biệt giữa Block, Proc và Lambda do mình tìm hiểu. Bài viết còn nhiều thiếu sót, mong các bạn cùng góp ý để bài viết hữu ích hơn cho mọi người.

Mình xin cảm ơn!

0