在Ruby中,当同时使用位置参数和关键字参数时,会有什么行为?

42
Ruby 2.0 支持关键字参数。我想知道,混合使用普通参数和关键字参数有什么“规则”?像这样的写法是不起作用的:
def some_method(a: 'first', b: 'second', c)
  [a, b, c]
end

但这将会:
def some_method(c, a: 'first', b: 'second')
  [a, b, c]
end

为什么在关键字参数之前(而不是之后)放置位置参数会起作用呢?
在网上有没有关于这个问题(混合使用关键字和位置参数)的参考资料?我还没有找到。
3个回答

64

顺序如下:

  • 必需参数
  • 带有默认值的参数(arg=default_value 表示法)
  • 可选参数(*args 表示法,有时称为“扩展参数”)
  • 再次是必需参数
  • 关键字参数
    • 可选的(arg:default_value 表示法,自 2.0.0 版本以来)
    • 与必需参数混合使用(arg: 表示法,自 2.1.0 版本以来)
  • 任意关键字参数(**args 表示法,自 2.0.0 版本以来)
  • 块参数(&blk 表示法)

例如:

def test(a, b=0, *c, d, e:1, f:, **g, &blk)
  puts "a = #{a}"
  puts "b = #{b}"
  puts "c = #{c}"
  puts "d = #{d}"
  puts "e = #{e}"
  puts "f = #{f}"
  puts "g = #{g}"
  puts "blk = #{blk}"
end

test(1, 2, 3, 4, 5, e:6, f:7, foo:'bar') { puts 'foo' }
# a = 1
# b = 2
# c = [3, 4]
# d = 5
# e = 6
# f = 7
# g = {:foo=>"bar"}
# blk = #<Proc:0x007fb818ba3808@(irb):24>

有关详细信息,请参阅官方Ruby语法文档


忘记了使用 ** 符号来表示“任意关键字参数”,所以我也添加了这些。 - Patrick Oscity
在 splat 之后,您还可以有强制参数。在 Ruby 2.1 中,您还可以拥有强制关键字参数,这些参数可以与可选关键字参数自由混合使用。请参阅 https://dev59.com/AWIj5IYBdhLWcg3wGRmM#20634180 - Jörg W Mittag
@JörgWMittag 谢谢!我也注意到了浏览文档的重要性。已将其添加到列表中。 - Patrick Oscity
2
谢谢,这对我比被采纳的答案更有帮助。我知道这说的更多是关于我而不是被采纳的答案,但就是这样 :) - sixty4bit

19
在Ruby中,用于参数列表的伪正则表达式(同样适用于方法、块和lambda字面值)类似于以下内容:
mand* opt* splat? mand* (mand_kw | opt_kw)* ksplat? block?

这里是一个例子:

def foo(m1, m2, o1=:o1, o2=:o2, *splat, m3, m4, 
          ok1: :ok1, mk1:, mk2:, ok2: :ok2, **ksplat, &blk)
  Hash[local_variables.map {|var| [var, eval(var.to_s)] }]
end

method(:foo).arity
# => -5

method(:foo).parameters
# => [[:req, :m1], [:req, :m2], [:opt, :o1], [:opt, :o2], [:rest, :splat], 
#     [:req, :m3], [:req, :m4], [:keyreq, :mk1], [:keyreq, :mk2], 
#     [:key, :ok1], [:key, :ok2], [:keyrest, :ksplat], [:block, :blk]]

foo(1, 2, 3, 4)
# ArgumentError: missing keywords: mk1, mk2

foo(1, 2, 3, mk1: 4, mk2: 5)
# ArgumentError: wrong number of arguments (3 for 4+)

foo(1, 2, 3, 4, mk1: 5, mk2: 6)
# => { m1: 1, m2: 2, o1: :o1, o2: :o2, splat: [], m3: 3, m4: 4, 
#      ok1: :ok1, mk1: 5, mk2: 6, ok2: :ok2, ksplat: {}, 
#      blk: nil }

foo(1, 2, 3, 4, 5, mk1: 6, mk2: 7)
# => { m1: 1, m2: 2, o1: 3, o2: :o2, splat: [], m3: 4, m4: 5, 
#      ok1: :ok1, mk1: 6, mk2: 7, ok2: :ok2, ksplat: {}, 
#      blk: nil }

foo(1, 2, 3, 4, 5, 6, mk1: 7, mk2: 8)
# => { m1: 1, m2: 2, o1: 3, o2: 4, splat: [], m3: 5, m4: 6, 
#      ok1: :ok1, mk1: 7, mk2: 8, ok2: :ok2, ksplat: {}, 
#      blk: nil }

foo(1, 2, 3, 4, 5, 6, 7, mk1: 8, mk2: 9)
# => { m1: 1, m2: 2, o1: 3, o2: 4, splat: [5], m3: 6, m4: 7, 
#      ok1: :ok1, mk1: 8, mk2: 9, ok2: :ok2, ksplat: {}, 
#      blk: nil }

foo(1, 2, 3, 4, 5, 6, 7, 8, mk1: 9, mk2: 10)
# => { m1: 1, m2: 2, o1: 3, o2: 4, splat: [5, 6], m3: 7, m4: 8, 
#      ok1: :ok1, mk1: 9, mk2: 10, ok2: :ok2, ksplat: {}, 
#      blk: nil }

foo(1, 2, 3, 4, 5, 6, 7, 8, ok1: 9, mk1: 10, mk2: 11)
# => { m1: 1, m2: 2, o1: 3, o2: 4, splat: [5, 6], m3: 7, m4: 8, 
#      ok1: 9, mk1: 10, mk2: 11, ok2: :ok2, ksplat: {}, 
#      blk: nil }

foo(1, 2, 3, 4, 5, 6, 7, 8, ok1: 9, mk1: 10, mk2: 11, ok2: 12)
# => { m1: 1, m2: 2, o1: 3, o2: 4, splat: [5, 6], m3: 7, m4: 8, 
#      ok1: 9, mk1: 10, mk2: 11, ok2: 12, ksplat: {}, 
#      blk: nil }

foo(1, 2, 3, 4, 5, 6, 7, 8, ok1: 9, mk1: 10, mk2: 11, ok2: 12, k3: 13)
# => { m1: 1, m2: 2, o1: 3, o2: 4, splat: [5, 6], m3: 7, m4: 8, 
#      ok1: 9, mk1: 10, mk2: 11, ok2: 12, ksplat: {k3: 13}, 
#      blk: nil }

foo(1, 2, 3, 4, 5, 6, 7, 8, 
      ok1: 9, mk1: 10, mk2: 11, ok2: 12, k3: 13, k4: 14)
# => { m1: 1, m2: 2, o1: 3, o2: 4, splat: [5, 6], m3: 7, m4: 8, 
#      ok1: 9, mk1: 10, mk2: 11, ok2: 12, ksplat: {k3: 13, k4: 14}, 
#      blk: nil }

foo(1, 2, 3, 4, 5, 6, 7, 8, 
      ok1: 9, ok2: 10, mk1: 11, mk2: 12, k3: 13, k4: 14) do 15 end
# => { m1: 1, m2: 2, o1: 3, o2: 4, splat: [5, 6], m3: 7, m4: 8, 
#      ok1: 9, mk1: 10, mk2: 11, ok2: 12, ksplat: {k3: 13, k4: 14}, 
#      blk: #<Proc:0xdeadbeefc00l42@(irb):15> }

[注意:Ruby 2.1将引入强制关键字参数,其余部分已经可以正常工作。]


8
如果你像我一样是新手,而且这个答案看不懂,请确保向下滚动到@PatrickOscity的答案。 - sixty4bit
哇,这就是我为什么如此讨厌 Ruby 的原因。这算是一门语言的怎样的介绍呢? - heeroyuy84
1
@heeroyuy84:你为什么认为这是关于Ruby的介绍?这不是关于Ruby的介绍,这只是一个问答网站上的回答。 - Jörg W Mittag

2
  1. 具有默认值和splat参数的参数必须分组;
  2. splat参数必须出现在具有默认值的位置参数之后,但在关键字参数之前;
  3. 关键字参数必须出现在位置参数之后,在双星号参数之前;
  4. 双星号参数必须出现在最后,但在块参数之前。

    def foo(a, b=1, c=2, *d, e, f: 1, g: 2, **kwargs, &block)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接