Sự khác nhau giữa Block, Proc, và Lamdba trong Ruby
Block, Proc, và Lamdba là gì ? Theo định nghĩa kỹ thuật : thì nó là ví dụ của closures trong Ruby. Có thể hiểu closures là một hàm được tạo ra từ bên trong một hàm khác (hàm cha), và nó có thể sử dụng các biến toàn cục, cục bộ của hàm cha và chính nó. Viết code kiểu closures có thể giúp code dễ ...
Block, Proc, và Lamdba là gì ?
- Theo định nghĩa kỹ thuật: thì nó là ví dụ của closures trong Ruby. Có thể hiểu closures là một hàm được tạo ra từ bên trong một hàm khác (hàm cha), và nó có thể sử dụng các biến toàn cục, cục bộ của hàm cha và chính nó. Viết code kiểu closures có thể giúp code dễ quản lý hơn và linh hoạt hơn trong việc xử lý.
- Theo định nghĩa đơn giản: thì nó là cách để chúng ta nhóm các đoạn mã muốn chạy vào một khối.
# Block [1,2,3].each { |x| puts x*2 } # block nằm trong dấu ngoặc nhọn [1,2,3].each do |x| puts x*2 # block cũng có thể nằm giữa do và end end # Proc p = Proc.new { |x| puts x*2 } [1,2,3].each(&p) # & sẽ nói với Ruby là đưa proc vào một block proc = Proc.new { puts "Hello World" } proc.call # bên trong của object Proc sẽ được thực thi khi sử dụng call # Lambda lam = lambda { |x| puts x*2 } [1,2,3].each(&lam) lam = lambda { puts "Hello World" } lam.call
Qua ví dụ trên, ta có thể thấy chúng khá là giống nhau và đơn giản, nhưng vẫn có sự khác nhau. Hãy cùng tìm hiểu phía dưới.
Khác nhau giữa Blocks và Procs
- Procs là đối tượng, còn Block thì không
Proc là một instance của class Proc
p = Proc.new { puts "Hello World" } p.call # prints 'Hello World' p.class # returns 'Proc' a = p # a now equals p, a Proc instance p # returns a proc object '#<Proc:0x007f96b1a60eb0@(irb):46>'
Ngược lại, một block chỉ là một phần của cú pháp trong method được gọi. Nó không đứng một mình được mà chỉ có thể đi cùng một list các đối số
{ puts "Hello World"} # syntax error a = { puts "Hello World"} # syntax error [1,2,3].each {|x| puts x*2} # chỉ làm việc như một phần cú phép của method được gọi
- Có thể truyền nhiều procs vào method
Vì proc là một đối tượng độc lập vì vậy ta có thể truyền nhiều procs như một tham số vào method. Còn block thì giới hạn hơn nhiều, nó chỉ có thể hoạt động khi đi kèm một list các đối số.
def multiple_procs(proc1, proc2) proc1.call proc2.call end a = Proc.new { puts "First proc" } b = Proc.new { puts "Second proc" } multiple_procs(a,b)
Khác nhau giữa Procs và Lambdas
Cả Procs và Lambdas đều là đối tượng của class Proc
proc = Proc.new { puts "Hello world" } lam = lambda { puts "Hello World" } proc.class # returns 'Proc' lam.class # returns 'Proc'
Tuy nhiên, chúng lại có một sự khác biệt nhỏ.
proc # returns '#<Proc:0x007f96b1032d30@(irb):75>' lam # returns '<Proc:0x007f96b1b41938@(irb):76 (lambda)>'
Sự khác biệt đấy là:
- Lambdas kiểm tra số lượng đối số, trong khi procs thì không
lam = lambda { |x| puts x } # tạo lambda với một đối số lam.call(2) # in ra 2 lam.call # ArgumentError: wrong number of arguments (0 for 1) lam.call(1,2,3) # ArgumentError: wrong number of arguments (3 for 1)
proc = Proc.new { |x| puts x } # tạo proc với một đối số proc.call(2) # in ra 2 proc.call # returns nil proc.call(1,2,3) # in ra 1, và 2 đối số còn lại sẽ bị bỏ qua
- Lambdas và Procs xử lý return khác nhau return được viết trong lambda sẽ chỉ có tác động outside khỏi lambda code
def lambda_test lam = lambda { return } lam.call puts "Hello world" end lambda_test # lambda_test sẽ vẫn in ra 'Hello World'
return được viết trong proc sẽ tác động outside luôn method tại điểm proc được thực thi
def proc_test proc = Proc.new { return } proc.call puts "Hello world" end proc_test # proc_test sẽ không in ra gì cả