¿Mezcla palabra clave con argumentos regulares en Ruby?

Ruby 2.0 admite argumentos de palabra clave. Me preguntaba, ¿cuáles son las ‘reglas’ para mezclar regularmente con argumentos de palabra clave? Algo como esto no funcionaría:

def some_method(a: 'first', b: 'second', c) [a, b, c] end 

pero esto:

 def some_method(c, a: 'first', b: 'second') [a, b, c] end 

Entonces, ¿por qué funciona poner un argumento regular antes de que los argumentos de la palabra clave (y no después) funcionen?

¿Hay alguna referencia en la web sobre esto (mezclando palabra clave y argumentos regulares)? Parece que no puedo encontrar ninguno.

Una pseudo-regex para listas de parámetros en Ruby (esto se aplica igualmente a los métodos, bloques y literales lambda) es algo como esto:

 mand* opt* splat? mand* (mand_kw | opt_kw)* ksplat? block? 

Aquí hay un ejemplo:

 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: # } 

[Nota: los argumentos de palabra clave obligatorios se introducirán en Ruby 2.1, todo lo demás ya funciona.]

El orden es el siguiente:

  • argumentos requeridos
  • argumentos con valores predeterminados ( arg=default_value notación de valores por defecto)
  • argumentos opcionales ( *args notación *args , algunas veces llamada “parámetro splat”)
  • argumentos requeridos, nuevamente
  • argumentos de palabra clave
    • opcional ( arg:default_value notación de valor arg:default_value , desde 2.0.0)
    • Entremezclado con requerido ( arg: notación, desde 2.1.0)
  • Argumentos de palabras clave arbitrarias ( **args notación **args , desde 2.0.0)
  • argumento de bloque ( &blk notación &blk )

Por ejemplo:

 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 = # 

Hay información más detallada disponible en la documentación oficial de Ruby Syntax .