Block, Proc, Lambda trong Ruby
Bài đăng này đã không được cập nhật trong 6 năm
I. Khái niệm
1. Block
- Block đơn giản là tập hợp các lệnh thành 1 khối, nằm trong
{...}
hoặcdo ... end
. - Có 1 quy ước chung là sử dụng
{...}
cho các block đơn (1 dòng lệnh) vàdo...end
cho các block bội (multi-line). Ví dụ:
[1, 2, 3].each do |n|
puts n*n
end
[1, 2, 3].each { |n| puts n*n }
- 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.
- Mọi thứ trong ruby đều là object ngoại trừ block.
- Block được truyền vào phương thức ở vị trí giống như là một tham số cuối cho phương thức được gọi và được gọi ra bằng câu lệnh
yield
trong định nghĩa phương thức. Ví dụ:
def sum
yield 3, 4
end
sum { |i, j| Sumary is: #{ i + j } }
- Khi sử dụng block thì có hạn chế đó là khi muốn thay đổi đầu vào thì chúng ta phải viết lại toàn bộ block để hiển thị giá trị cho input mới.
Proc
- Proc là 1 object, cũng có thể nói Proc là 1 Block được đặt tên.
- Chúng ta có thể lưu trữ 1 Proc trong 1 biến và sau đó truyền nó 1 cách rõ ràng tới bất kỳ method nào gọi nó. Ví dụ:
p = Proc.new { |n| puts n*n }
[1, 2, 3].each(&p)
[4, 5, 6].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/
proc = Proc.new do
puts "Hello"
end
proc.call
# Chúng ta có thể gọi Proc bằng phương thức .call
- Proc là 1 Proc object.
Lambda
- Lambda là một function và không có tên cụ thể.
- Nó có thể được sử dụng để gán 1 đoạn code.
- Là 1 object
- Những gì lambda làm hoàn toàn độc lập với fucntion gọi nó.
- Lambda là 1 Proc object.
- Lambda thể hiện tính chất của một method
- Ví dụ:
result = lambda { |x| x + 1 }
hoặc
result = -> { |x| x + 1 }
Dùng .call để gọi
result.call(10)
II. So sánh
Block và Proc
- Proc là objects, còn Block thì không
- 1 proc là thể hiện của 1 lớp Proc
- 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ả.
- Chỉ truyền được 1 block vào trong danh sách đối số của methods, còn với proc thì có thể truyền nhiều proc vào methods.
Proc và Lambda
- Đều là Proc object
- Lambda kiểm tra số lượng các tham số của nó nhận và trả về một
ArgumentError
nếu số lượng đó không phù hợp với số lượng đối số trong method của nó; còn Proc thì nếu không truyền tham số thì proc mặc định tham số đó bằng nil.
p = Proc.new { |x| puts x +1 }
p.call(1, 2)
# return 2
l = lambda { |x| puts x +1 }
l.call(1, 2)
# return Argument Error
- Đối với hàm dùng return trong lambda và proc thì với proc thì sẽ return ngay sau khi thực hiện xong proc, còn với lambda thì vẫn tiếp tục chạy hết hàm sau khi gọi xong lambda.
def method_lambda
lam = lambda { return puts "xin chao" }
lam.call
puts "cac ban"
end
# khi gọi lambda trên
method_lambda
# kết quả in ra là
xin chao
cac ban
def method_proc
proc = Proc.new { return puts "xin chao" }
proc.call
puts "cac ban"
end
# gọi proc trên
method_proc
# kết quả in ra là
xin chao
- Việc dùng proc hay lambda phụ thuộc việc có dùng return hay không.
Qua so sánh 3 loại trên, ta có thế rút ra được các đặc trưng của từng loại :
- Procs là object còn block thì không.
- Hầu hết block xuất hiện trong một danh sách các đối số (argument).
- Lambda kiểm tra số lượng đối số còn proc thì không.
- Lambda và proc xử lý với return không giống nhau.
Hy vọng sau bài viết này các bạn sẽ có cái nhìn rõ hơn về các khái niệm Block, Proc & Lambda!
Nguồn:
http://code.tutsplus.com/tutorials/ruby-on-rails-study-guide-blocks-procs-and-lambdas--net-29811
http://www.reactive.io/tips/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/
All rights reserved