Computer >> Máy Tính >  >> Lập trình >> Ruby

Hướng dẫn cơ bản về Blocks, Procs &Lambdas

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 procslambdas khác nhau:

  • Lambdas được xác định bằng -> {} và procs với Proc.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_proccall_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 🙂