17

Ruby 2.0.0 支持关键字参数 (KA),我想知道在纯 Ruby 的上下文中这个特性的好处/用例是什么,尤其是考虑到由于关键字匹配需要每次都进行的性能损失时调用带有关键字参数的方法的时间。

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)
4

4 回答 4

16

关键字参数有一些没有人提到的明显优势。

首先,您与参数的顺序无关。所以在你偶尔可能有一个 nil 参数的情况下,它看起来更干净:

def print_greeting(name, message = "Hello")
  puts "#{message}, #{name}"
end

print_greeting("John Connor", "Hasta la vista") 

如果您使用关键字参数:

def print_greeting(message: "Hello", name:)
  puts "#{message}, #{name}"
end

print_greeting(message: "Hasta la vista", name: "John Connor") 

甚至

print_greeting(name: "John Connor", message: "Goodbye")

它消除了必须记住参数顺序的需要。但是,缺点是您必须记住参数的名称。这应该或多或少直观,并且可以说会导致更仔细考虑的方法签名。

使用关键字参数的另一个好处是,当您有一个将来可能需要额外参数的方法时。

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

如果您的系统要求现在需要了解一个人最喜欢的糖果棒,或者他们是否超重(因为食用了太多他们最喜欢的糖果棒),该怎么办?你怎么能使用关键字 args 来做到这一点?简单的:

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

在关键字参数之前总是有哈希,但这导致了更多的样板代码来提取和分配变量。样板代码 == 更多打字 == 更多潜在错别字 == 更少时间编写出色的 ruby​​ 代码。

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

如果您只是设置一个采用一个参数并且很可能永远不需要更改的方法:

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

然后应避免使用关键字参数,因为对性能的影响很小。

于 2015-01-28T17:22:20.567 回答
5

从 ruby​​-2.2.0 开始,使用关键字参数的低效率问题似乎不再是问题。

功能 #10440修复了速度问题并在ruby​​-2.2.0中发布:

2014 年 11 月 3 日星期一 03:02:38 笹田光一

  • 重写方法/块参数拟合逻辑以优化关键字参数/参数和 splat 参数。功能 #10440(此票证中描述了详细信息)

您可以自己查看(使用与原始问题中给出的相同代码):

(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)

使用关键字 args 的性能损失仍然可以忽略不计,但我认为这是一个可以接受的权衡,以换取提高可读性和位置灵活性的好处。

于 2016-01-14T21:53:09.853 回答
4

由于 KA 是 ruby​​ 范围内的创新,我看到了两个主要优势:

  • 将允许的参数限制为预定义的集合,就像 Rails 对assert_valid_keys所做的那样;
  • 在代码块中使用该功能。

总结:

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
于 2013-02-25T09:17:24.610 回答
1

例如

一个函数

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

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

可以写

def welcome_message(message, name: 'hoge')
  "#{message}、#{name}"
end
于 2013-02-25T08:15:30.397 回答