在Ruby中何时使用关键字参数又称命名参数

在Ruby中何时使用关键字参数又称命名参数,ruby,named-parameters,keyword-argument,Ruby,Named Parameters,Keyword Argument,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.t

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)
比如说

函数

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

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

  • 将允许的参数限制为预定义的集合,就像Rails所做的那样
  • 在代码块中使用该功能
总结如下:

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

关键字参数有几个明显的优点,没有人提到过

首先,你不耦合到参数的顺序。因此,在一个你可能偶尔会有一个零争论的情况下,它看起来更干净了:

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来实现这一点? 简单:

在关键字参数出现之前,总是有散列,但这导致了提取和分配变量的更多样板代码。样板代码==更多类型==更多潜在的打字错误==更少的时间编写出色的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

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

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

修复了速度问题,并于以下时间发布:

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

  • 重写方法/块参数拟合逻辑以优化关键字参数/参数和splat参数。(详情见本票)
您可以自己看到这一点(使用与原始问题相同的代码):


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

补充回答,当您需要将参数分配给方法定义中的变量时,请使用它们,并避免自己编写自定义代码。请不要欺骗我们<代码>定义欢迎消息(消息,选项={:name=>'hoge'});“{message}、{options[:name]}”;end是
欢迎消息的一行。然而,这种方法的主要缺点是,要么需要使用某种
key\u valid?
扩展
Hash
,要么希望传递的密钥是正确的。KA方法可以精确地指定哪些键是允许的。@mudasobwa在我看来你错了;使用您的函数,
welcome_message(“message”,{:age=>22})
不会在结果中显示no
hoge
,而是使用
关键字
。函数为整个散列设置了一个默认值,而不是为散列的已定义键设置了一个默认值。@oldergod也许我的问题不是很清楚。我正在寻找令人信服的理由来使用此功能。您所提供的更像是一个一次性案例,给我的好处是代码少了2行,而不是更多。例如,在像c#这样的语言中,KA的一个明显优势是,它只有一个方法定义,并且没有编写几十个不同的方法签名来实现
方法重载
,而在Ruby中,方法重载不是一个有效的范例,因此这一优势并不相关。另一个好处是与Ruby相关的
代码可读性
。是否可以编写一个能够同时处理命名参数和有序参数的初始化方法?
def say_full_name(first_name, last_name)
  puts "#{first_name} #{last_name}"
end
(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)