When to use keyword arguments aka named parameters in Ruby

痞子三分冷 提交于 2019-12-19 05:08:51

问题


Ruby 2.0.0 supports keyword arguments (KA) and I wonder what the benefits/use-cases are of this feature in context of pure Ruby, especially when seen in light of the performance penalty due to the keyword matching that needs to be done every time a method with keyword arguments is called.

require 'benchmark'

def foo(a:1,b:2,c:3)
  [a,b,c]
end

def bar(a,b,c)
  [a,b,c]
end

number = 1000000
Benchmark.bm(4) do |bm|
  bm.report("foo") { number.times { foo(a:7,b:8,c:9)  } }
  bm.report("bar") { number.times { bar(7,8,9) } }
end

#           user     system      total        real
# foo    2.797000   0.032000   2.829000 (  2.906362)
# bar    0.234000   0.000000   0.234000 (  0.250010)

回答1:


Keyword arguments have a few distinct advantages no one has touched on.

First off you are not coupled to the order of the arguments. So in a case where you might have a nil argument occasionally it looks a lot cleaner:

def yo(sup, whats="good", dude="!")
  # do your thing
end

yo("hey", nil, "?")

if you use keyword arguments:

def yo(sup:, whats:"good", dude:"!")
  # do your thing
end

yo(sup: "hey", dude: "?")

or even

yo(dude: "?", sup: "hey")

It removes the need to have to remember the order of the arguments. However, the disadvantage is you have to remember the argument's name, but that should be more or less intuitive.

Also, when you have a method that could possibly have a need to take more arguments in the future.

def create_person(name:, age:, height:)
  # make yourself some friends
end

what if your system all of the sudden wants to know about a person's favorite candy bar, or if they are overweight (from consuming too many of their favorite candy bar), how would you do that? Simple:

def create_person(name:, age:, height:, favorite_candy:, overweight: true)
  # make yourself some fat friends
end

Before keyword arguments there was always the hash, but that led to a lot more boilerplate code to extract and assign variable. Boilerplate code == more typing == more potential typos == less times writing awesome ruby code.

def old_way(name, opts={})
  age    = opts[:age]
  height = opts[:height]
  # all the benefits as before, more arthritis and headaches  
end

If you are just setting up a method that takes one argument and will most likely never have a need to change:

def say_full_name(first_name, last_name)
  puts "#{first_name} #{last_name}"
end

Then keyword arguments should be avoided, since there is a small performance hit.




回答2:


Since KA are ruby-wide innovation, I see two main advantages:

  • limit permitted arguments to a predefined set, as Rails does with assert_valid_keys;
  • use the feature within code blocks.

The summing up:

a = lambda { |name: "Leonardo", age: 67| [name, age] }
a.call # ⇒ ["Leonardo", 67]
a.call name: "Michelangelo", age: 88 # ⇒ ["Michelangelo", 88]
a.call name: "Schwarzenegger", alive: true # ⇒ ArgumentError: unknown keyword: alive



回答3:


The inefficiency issue of using keyword arguments no longer seems to be a problem as of ruby-2.2.0.

Feature #10440 fixed the speed issue and was released in ruby-2.2.0:

Mon Nov 03 03:02:38 2014 Koichi Sasada

  • rewrite method/block parameter fitting logic to optimize keyword arguments/parameters and a splat argument. Feature #10440 (Details are described in this ticket)

You can see this for yourself (using the same code as given in the original question):

(08:04:%) rvm use ruby-2.0.0-p247
Using /Users/adam/.rvm/gems/ruby-2.0.0-p247

(08:04:%) ruby keyword_benchmarks.rb

       user     system      total        real
foo    1.390000   0.060000   1.450000 (  1.451284)
bar    0.130000   0.000000   0.130000 (  0.122344)

(08:04:%)   rvm use ruby-2.2.0
Using /Users/adam/.rvm/gems/ruby-2.2.0

(08:04:%) ruby keyword_benchmarks.rb

       user     system      total        real
foo    0.140000   0.000000   0.140000 (  0.136194)
bar    0.110000   0.000000   0.110000 (  0.116246)

There's still an extremely negligible performance penalty for using keyword args, but I think it's an acceptable tradeoff in exchange for the benefit of increased readability and positional flexibility.




回答4:


For example

A function

def welcome_message(message, options={})
  default_options = {name: 'hoge'}
  options = default_options.merge(options)

  "#{message}、#{options[:name]}"
end

could be written

def welcome_message(message, name: 'hoge')
  "#{message}、#{name}"
end


来源:https://stackoverflow.com/questions/15062570/when-to-use-keyword-arguments-aka-named-parameters-in-ruby

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!