Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Ruby

Làm chủ đối số từ khóa, Hash và toán tử splat trong Ruby

Bạn có thể đã thấy pattern này trước. Một phương thức có hash options là đối số cuối, giữ tham số thêm:

def hello_message(name_parts = {}) first_name = name_parts.fetch(:first_name) last_name = name_parts.fetch(:last_name) "Hello, #{first_name} #{last_name}" end

Thật không may, bạn cần trích xuất tham số đó ra khỏi hash. Và nghĩa là có nhiều setup phải lội qua trước khi đến phần tốt.

Nhưng nếu bạn thay đổi phương thức này dùng keyword arguments trong Ruby 2.0+, bạn sẽ không phải kéo :first_name và :last_name ra khỏi hash. Ruby làm nó cho bạn:

def hello_message(first_name:, last_name:) "Hello, #{first_name} #{last_name}" end

Thậm chí tốt hơn, nếu ứng dụng của bạn dùng Ruby 1.9+ hash syntax, phương thức của bạn có thể dùng keyword arguments mà không cần thay đổi caller của phương thức đó:

def hello_message_with_an_options_hash(name_parts = {}) first_name = name_parts.fetch(:first_name) last_name = name_parts.fetch(:last_name) "Hello, #{first_name} #{last_name}" end def hello_message_with_keyword_arguments(first_name:, last_name:) "Hello, #{first_name} #{last_name}" end hello_message_with_an_options_hash(first_name: "Justin", last_name: "Weiss") hello_message_with_keyword_arguments(first_name: "Justin", last_name: "Weiss")

Thấy không? Những đối số đó giống hệt nhau!

Đẩy cú pháp keyword argument một bước quá xa

Điều gì nếu bạn chưa chuyển sang cú pháp Hash mới? Bạn có thể chuyển đổi tất cả mã. Nhưng, ít nhất trong Ruby 2.2.1, cú pháp hash cũ hoạt động tốt với keyword arguments:

irb(main):007:0> hello_message_with_keyword_arguments(:first_name => "Justin", :last_name => "Weiss") => "Hello, Justin Weiss"

Tốt! Còn truyền một hash object, thay vì đối số?

irb(main):008:0> options = {:first_name => "Justin", :last_name => "Weiss"} irb(main):009:0> hello_message_with_keyword_arguments(options) => "Hello, Justin Weiss"

Wow. Điều gì nếu chúng ta muốn trộn hash và keyword arguments?

irb(main):010:0> options = {last_name: "Weiss"} irb(main):011:0> hello_message_with_keyword_arguments(first_name: "Justin", options) SyntaxError: (irb):11: syntax error, unexpected ')', expecting => from /usr/local/bin/irb:11:in `<main>'

OK. Tôi đoán chúng ta đẩy đó một bước quá xa. Để sửa, bạn có thể dùng Hash#merge để xây dựng hash bạn có thể truyền riêng. Nhưng có cách tốt hơn.

Nếu bạn dùng đối số thường thay vì keyword arguments, bạn có thể splat đối số từ Array, dùng *:

def generate_thumbnail(name, width, height) # ... end dimensions = [240, 320] generate_thumbnail("headshot.jpg", *dimensions)

Nhưng có cách để splat keyword arguments vào danh sách đối số?

Hóa ra có: **. Đây là cách bạn sửa ví dụ hỏng kia với **:

irb(main):010:0> options = {last_name: "Weiss"} irb(main):011:0> hello_message_with_keyword_arguments(first_name: "Justin", **options) => "Hello, Justin Weiss"

Và nếu bạn thực sự điên rồ, bạn có thể trộn đối số thường, keyword arguments, và splats:

def hello_message(greeting, time_of_day, first_name:, last_name:) "#{greeting} #{time_of_day}, #{first_name} #{last_name}!" end args = ["Morning"] keyword_args = {last_name: "Weiss"} hello_message("Good", *args, first_name: "Justin", **keyword_args) # => "Good Morning, Justin Weiss!"

Tất nhiên, nếu bạn thấy bản thân trong tình huống đó cần thiết, bạn có thể đã làm sai từ rất sớm!

Bắt keyword arguments cách dễ dàng

Bạn biết làm sao bạn có thể biến tất cả đối số phương thức thành mảng dùng *?

def argument_capturing_method(*args) args end argument_capturing_method(1, 2, 3) # => [1, 2, 3]

Điều này cũng hoạt động với keyword arguments. Chúng được chuyển thành hash, và xuất hiện như đối số cuối của mảng args:

argument_capturing_method(1, 2, 3, key: "value") # => [1, 2, 3, {:key=>"value"}]

Nhưng args.last[:key] không phải cách tốt nhất đọc keyword arguments lấy cách đó. Thay vào đó, bạn có thể dùng cú pháp ** mới để lấy keyword arguments riêng:

def dual_argument_capturing_method(*args, **keyword_args) {args: args, keyword_args: keyword_args} end dual_argument_capturing_method(1, 2, 3, key: "value") # => {:args=>[1, 2, 3], :keyword_args=>{:key=>"value"}}

Với cú pháp này, bạn có thể truy cập đối số thường đầu tiên với args[0] và keyword argument :key với keyword_args[:key].

… Tất nhiên, bây giờ chúng ta quay lại options hash.

Keyword arguments tuyệt vời cho việc loại bỏ ton boilerplate trích xuất tham số từ mã của bạn. Và bạn có thể thậm chí không phải thay đổi bất kỳ mã nào để tận dụng chúng.

Nhưng khi bạn viết phương thức generic hơn, có một số kỹ thuật mới bạn sẽ phải học để xử lý keyword arguments tốt. Bạn có thể không phải dùng kỹ thuật đó thường xuyên. Nhưng khi bạn làm, quyền lực và sự linh hoạt đó sẽ ở đó, chờ bạn.