Khối ruby, procs và lambdas.
Chúng là gì?
Chúng hoạt động như thế nào?
Chúng khác nhau như thế nào?
Bạn sẽ học được điều đó &nhiều hơn thế nữa khi đọc bài đăng này!
Nội dung
- 1 Hiểu các Khối Ruby
- 2 Từ khoá Lợi nhuận Ruby
- 3 Chặn ẩn ý so với rõ ràng
- 4 Cách kiểm tra xem một khối đã được đưa ra hay chưa
- 5 Lambda là gì?
- 6 Lambdas và Procs
- 7 Đóng cửa
- 8 Lớp Ràng buộc
- 9 Hướng dẫn bằng Video
- 10 Kết thúc
- 10.1 Có liên quan
Hiểu về các khối Ruby
Các khối Ruby là các hàm ẩn danh nhỏ có thể được chuyển vào các phương thức.
Các khối được bao trong một do / end
câu lệnh hoặc giữa các dấu ngoặc nhọn {}
và chúng có thể có nhiều đối số.
Tên đối số được xác định giữa hai dấu ngoặc kép |
ký tự.
Nếu bạn đã sử dụng each
trước đó, sau đó bạn đã sử dụng khối!
Đây là một ví dụ :
# Form 1: recommended for single line blocks [1, 2, 3].each { |num| puts num } ^^^^^ ^^^^^^^^ block block arguments body
# Form 2: recommended for multi-line blocks [1, 2, 3].each do |num| puts num end
Khối Ruby rất hữu ích vì nó cho phép bạn lưu một chút logic (mã) và sử dụng nó sau này.
Điều này có thể là một cái gì đó giống như ghi dữ liệu vào một tệp, so sánh xem một phần tử này có bằng một phần tử khác hoặc thậm chí in một thông báo lỗi.
Từ khóa lợi nhuận của Ruby
yield
nghĩa là trong Ruby?
Lợi nhuận là một từ khóa Ruby gọi một khối khi bạn sử dụng nó.
Đó là cách các phương pháp sử dụng khối!
Khi bạn sử dụng yield
từ khóa, mã bên trong khối sẽ chạy &thực hiện công việc của nó.
Giống như khi bạn gọi một phương thức Ruby thông thường.
Đây là một ví dụ :
def print_once yield end print_once { puts "Block is being run" }
Thao tác này chạy bất kỳ khối nào được chuyển đến print_once
kết quả là "Block is being run"
sẽ được in trên màn hình.
Bạn có biết…
yield
đó có thể được sử dụng nhiều lần?
Mỗi khi bạn gọi yield
, khối sẽ chạy, vì vậy điều này giống như gọi lại cùng một phương thức.
Ví dụ :
def print_twice yield yield end print_twice { puts "Hello" } # "Hello" # "Hello"
Và cũng giống như các phương pháp…
Bạn có thể chuyển bất kỳ số lượng đối số nào cho yield
.
Ví dụ :
def one_two_three yield 1 yield 2 yield 3 end one_two_three { |number| puts number * 10 } # 10, 20, 30
Những đối số này sau đó trở thành đối số của khối.
Trong ví dụ này number
.
Khối ẩn ý so với khối rõ ràng
Các khối có thể là "rõ ràng" hoặc "ngầm".
Rõ ràng có nghĩa là bạn đặt tên cho nó trong danh sách tham số của mình.
Bạn có thể chuyển một khối rõ ràng sang một phương thức khác hoặc lưu nó vào một biến để sử dụng sau này.
Đây là một ví dụ :
def explicit_block(&block) block.call # same as yield end explicit_block { puts "Explicit block called" }
Lưu ý &block
tham số…
Đó là cách bạn xác định tên của khối!
Cách kiểm tra xem có khối nào được đưa ra hay không
Nếu bạn cố gắng yield
không có khối, bạn sẽ nhận được no block given (yield)
lỗi.
Bạn có thể kiểm tra xem một khối đã được chuyển vào hay chưa bằng block_given?
phương pháp.
Ví dụ :
def do_something_with_block return "No block given" unless block_given? yield end
Điều này ngăn ngừa lỗi nếu ai đó gọi phương thức của bạn mà không có khối.
Lambda là gì?
Lambda là một cách để xác định một khối và các tham số của nó bằng một số cú pháp đặc biệt.
Bạn có thể lưu lambda này vào một biến để sử dụng sau này.
Cú pháp để xác định một lambda Ruby trông như thế này:
say_something = -> { puts "This is a lambda" }
Bạn cũng có thể sử dụng cú pháp thay thế:
lambda
thay vì->
.
Việc xác định lambda sẽ không chạy mã bên trong nó, giống như việc xác định một phương thức sẽ không chạy phương thức đó, bạn cần sử dụng lệnh gọi call
phương pháp cho điều đó.
Ví dụ :
say_something = -> { puts "This is a lambda" } say_something.call # "This is a lambda"
Có những cách khác để call
một lambda
, thật tốt khi biết chúng tồn tại, tuy nhiên, tôi khuyên bạn nên gắn bó với call
để rõ ràng.
Đây là danh sách :
my_lambda = -> { puts "Lambda called" } my_lambda.call my_lambda.() my_lambda[] my_lambda.===
Lambdas cũng có thể nhận các đối số, đây là một ví dụ:
times_two = ->(x) { x * 2 } times_two.call(10) # 20
Nếu bạn chuyển sai số đối số cho lambda
, nó sẽ đưa ra một ngoại lệ, giống như một phương thức thông thường.
Lambdas vs Procs
Procs là một khái niệm rất giống nhau…
Một trong những điểm khác biệt là cách bạn tạo chúng.
Ví dụ :
my_proc = Proc.new { |x| puts x }
Không có Lambda
chuyên dụng lớp. Một lambda
chỉ là một Proc
đặc biệt sự vật. Nếu bạn xem qua các phương thức phiên bản từ Proc
, bạn sẽ nhận thấy có lambda?
phương pháp.
Bây giờ :
Một proc hoạt động khác với lambda, đặc biệt khi nói đến các đối số:
t = Proc.new { |x,y| puts "I don't care about arguments!" } t.call # "I don't care about arguments!"
Một sự khác biệt khác giữa procs
&lambdas
là cách họ phản ứng với return
tuyên bố.
Một lambda
sẽ return
bình thường, giống như một phương pháp thông thường.
Nhưng một proc
sẽ cố gắng return
từ bối cảnh hiện tại.
Đây là ý của tôi :
Nếu bạn chạy mã sau, bạn sẽ thấy cách proc
tăng LocalJumpError
ngoại lệ.
Lý do là bạn không thể return
từ ngữ cảnh cấp cao nhất.
Hãy thử điều này :
# 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}"
Nếu proc
ở bên trong một phương thức, sau đó gọi return
sẽ tương đương với việc trả về từ phương thức đó.
Điều này được chứng minh trong ví dụ sau.
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"
Dưới đây là tóm tắt về cách procs
và lambdas
khác nhau:
- Lambdas được xác định bằng
-> {}
và procs vớiProc.new {}
. - Procs trả về từ phương thức hiện tại, trong khi lambdas trả về từ chính lambda.
- Procs không quan tâm đến số lượng đối số chính xác, trong khi lambdas sẽ đưa ra một ngoại lệ.
Nhìn vào danh sách này, chúng ta có thể thấy rằng lambdas
gần với một phương thức thông thường hơn nhiều so với procs
đang.
Đóng cửa
Các procs &lambdas của Ruby cũng có một thuộc tính đặc biệt khác. Khi bạn tạo một proc Ruby, nó sẽ nắm bắt phạm vi thực thi hiện tại với nó.
Khái niệm này, đôi khi được gọi là bao đóng, có nghĩa là proc
sẽ mang theo các giá trị như biến cục bộ và phương thức từ ngữ cảnh nơi nó được xác định.
Chúng không mang các giá trị thực mà là một tham chiếu đến chúng, vì vậy nếu các biến thay đổi sau khi tạo proc, thì proc sẽ luôn có phiên bản mới nhất.
Hãy xem một ví dụ :
def call_proc(my_proc) count = 500 my_proc.call end count = 1 my_proc = Proc.new { puts count } p call_proc(my_proc) # What does this print?
Trong ví dụ này, chúng tôi có count
cục bộ biến, được đặt thành 1
.
Chúng tôi cũng có một proc có tên là my_proc
và call_proc
phương thức chạy (thông qua call
method) bất kỳ proc hoặc lambda nào được chuyển vào dưới dạng đối số.
Bạn nghĩ chương trình này sẽ in gì?
Nó có vẻ giống như 500
là kết luận hợp lý nhất, nhưng do hiệu ứng "đóng cửa", điều này sẽ in ra 1
.
Điều này xảy ra vì proc đang sử dụng giá trị của count
từ vị trí mà proc được xác định và nằm ngoài định nghĩa phương pháp.
Lớp Ràng buộc
Ruby procs &lambdas lưu trữ thông tin phạm vi này ở đâu?
Để tôi cho bạn biết về Binding
lớp…
Khi bạn tạo Binding
đối tượng thông qua binding
, bạn đang tạo một "anchor" cho đến thời điểm này trong mã.
Mọi biến, phương thức &lớp được xác định tại thời điểm này sẽ có sẵn sau đó thông qua đối tượng này, ngay cả khi bạn đang ở trong một phạm vi hoàn toàn khác.
Ví dụ :
def return_binding foo = 100 binding end # Foo is available thanks to the binding, # even though we are outside of the method # where it was defined. puts return_binding.class puts return_binding.eval('foo') # If you try to print foo directly you will get an error. # The reason is that foo was never defined outside of the method. puts foo
Nói cách khác, thực thi một cái gì đó trong ngữ cảnh của một binding
đối tượng giống như thể mã đó ở cùng một nơi mà binding
đó đã được xác định (hãy nhớ ẩn dụ "neo").
Bạn không cần sử dụng binding
trực tiếp đối tượng, nhưng vẫn tốt nếu biết đây là một vật 🙂
Hướng dẫn bằng video
Kết thúc
Trong bài đăng này, bạn đã học cách khối hoạt động, sự khác biệt giữa Ruby procs &lambdas và bạn cũng đã biết về hiệu ứng "đóng" xảy ra bất cứ khi nào bạn tạo một khối.
Một điều tôi không đề cập đến là phương pháp cà ri.
Phương thức này cho phép bạn chuyển một số hoặc tất cả các đối số bắt buộc.
Nếu bạn chỉ truyền một phần số đối số, bạn sẽ nhận được một proc mới với các đối số này đã được "tải sẵn", khi tất cả các đối số được cung cấp thì proc sẽ được thực thi.
Tôi hy vọng bạn thích bài đăng này!
Đừng quên đăng ký vào biểu mẫu bên dưới và chia sẻ điều này với bạn bè của bạn 🙂