5

我有一个方法,其中包含很长的可选参数列表,例如:

def foo(foo = nil, bar = nil, baz = nil, qux = nil)
    # no-op
end

我认为调用该方法并将拆分哈希作为参数传递将通过将键与方法参数匹配来将哈希项映射到参数:

params = { bar: 'bar', foo: 'foo' }
foo(*params)

不幸的是,当我在使用拆分哈希调用方法后检查局部变量时,如果传入拆分数组,我得到的正是我所期望的,但这不是我所希望的:

foo == [:bar, 'bar'] # hoped: foo == 'foo'
bar == [:foo, 'foo'] # hoped: bar == 'bar'

我在这里缺少什么?

4

2 回答 2

8

(这个答案是指提出问题时当前的 Ruby 版本。请参阅编辑以了解今天的情况。)

Ruby 不支持按名称传递参数。splat 运算符( )通过*调用任意可枚举对象来扩展to_ary它,并将结果拼接到参数列表中。在您的情况下,您传入的可枚举是一个哈希,它被转换为一个键值对数组:

[2] pry(main)> params.to_a
=> [[:bar, "bar"], [:foo, "foo"]]

所以函数的前两个参数将是值[:bar, "bar"][:foo, "foo"](不管它们的参数名称!)。

如果你想在 Ruby 中使用类似于关键字参数的东西,你可以利用在将哈希作为最后一个参数传递给函数时不需要大括号的事实:

def foo(opts = {})
    bar = opts[:bar] || <default>
    foo = opts[:foo] || <default>
    # or with a lot of parameters:
    opts = { :bar => <default>, :foo => <default>, ... }.merge(opts)
end

foo(foo: 3)  # equivalent to foo({ foo: 3 })

编辑

从 2.0 版开始,Ruby 现在支持具有专用语法的命名参数。感谢用户jgoyon指出这一点。

于 2012-08-26T10:58:07.607 回答
2

这个问题被称为命名参数。因为 Ruby 没有命名参数,所以这是处理它的经典方法:

def foo(options = {})
  options = {:foo => nil, :bar => nil, :baz => nil, qux => nil}.merge(options)
  ..
end

或使用ActiveSupport::CoreExtensions::Hash::ReverseMergefromRails

def foo(options = {})
  options.reverse_merge!{:foo => nil, :bar => nil, :baz => nil, qux => nil}
  ..
end

在未来的 Ruby 2.0 版本中,可以使用命名参数

def foo(foo: nil, bar: nil, baz: nil, qux: nil)
  puts "foo is #{foo}, bar is #{bar}, baz is #{baz}, ..."
  ..
end
于 2012-08-26T11:15:10.267 回答