Blocks, Procs & Lambdas
Bài đăng này đã không được cập nhật trong 4 năm
1. Understanding Blocks
Block rất phổ biến ở Ruby, bạn có thể nghĩ chúng là những hàm số vô danh có thể được truyền vào các phương thức.
Các block được đặt trong câu lệnh do / end hoặc giữa các dấu ngoặc {}, và chúng có thể có nhiều đối số. Các tên đối số được định nghĩa giữa ||.
Ví dụ:
[1, 2, 3].each { |num| puts num }
Làm thế nào một method làm việc với block, và làm thế nào nó có thể biết nếu một block đã có sẵn?
Vâng, để trả lời câu hỏi đầu tiên, bạn cần sử dụng từ khóa yield
. Khi bạn sử dụng yield
, code bên trong block sẽ được thực hiện.
Ví dụ:
def print_once
yield
end
print_once { puts "Block is being run" }
Yield có thể được sử dụng nhiều lần:
def print_twice
yield
yield
end
print_twice { puts "Hello" }
Hoặc sử dụng yield với tham số:
def one_two_three
yield 1
yield 2
yield 3
end
one_two_three { |number| puts number * 10 }
# 10, 20, 30
Truyển block như 1 tham số của hàm:
def explicit_block &block
block.call # Same as yield
end
explicit_block { puts "Explicit block called" }
2. Lambdas vs Procs
Procs và lambdas có khái niệm rất giống nhau, một trong những khác biệt là cách bạn tạo chúng.
Trên thực tế, không có lớp Lambda chuyên dụng. Một lambda chỉ là một object Proc đặc biệt. Nếu bạn hãy xem các method instance từ Proc, bạn sẽ nhận thấy có một method lambda?
say_something = -> { puts "This is a lambda" }
Chúng ta có thể sử dụng lambda thay vì ->.
Lambda sẽ không chạy nếu không dùng method call
Ví dụ:
lam = -> {p "Hello World"}
lam.call
# "Hello World"
Ngoài ra, ta có các cách khác để gọi lambda như sau:
my_lambda = -> { puts "Lambda called" }
my_lambda.call
my_lambda.()
my_lambda[]
my_lambda.===
Truyền đối số vào lambda:
times_two = ->(x) { x * 2 }
times_two.call(10)
# 20
Nếu chúng ta truyền sai đối số, lambda sẽ ném ra ngoại lệ. Nhưng Proc thì không:
t = Proc.new { |x,y| puts "I don't care about arguments!" }
t.call
# "I don't care about arguments!"
Cùng chạy đoạn code sau:
# Should work
my_lambda = -> { return 1 }
puts "Lambda result: #{my_lambda.call}"
# Should raise exception
my_proc = Proc.new { return 1 }
puts "Proc result: #{my_proc.call}"
Proc sẽ return khỏi method mà không cần quan tâm đoạn code còn lại:
def call_proc
puts "Before proc"
my_proc = Proc.new { return 2 }
my_proc.call
puts "After proc"
end
p call_proc
# Prints "Before proc" but not "After proc"
Những điểm khác nhau giữa Proc và lambda:
- Lambdas được định nghĩa bởi-> {} and procs bởi Proc.new {}.
- Proc trả về current method, lambda trả về chính nó.
- Proc không quan tâm argument còn Lambda thì có.
All rights reserved