Подтвердить что ты не робот

Смешивание ключевого слова с регулярными аргументами в Ruby?

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

Итак, почему возникает регулярный аргумент перед тем, как работают аргументы ключевого слова (а не после)?

Есть ли в Интернете какая-то ссылка на это (смешивание ключевого слова и регулярных аргументов)? Кажется, я не могу найти.

4b9b3361

Ответ 1

Псевдо-регулярное выражение для списков параметров в Ruby (это в равной степени относится к методам, блокам и лямбда-литералам) выглядит примерно так:

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:[email protected](irb):15> }

[Примечание: обязательные аргументы ключевого слова будут введены в Ruby 2.1, все остальные уже работают.]

Ответ 2

Порядок выглядит следующим образом:

  • требуемые аргументы
  • с значениями по умолчанию (arg=default_value нотация)
  • необязательные аргументы (*args обозначение, иногда называемое "параметром splat" )
  • требуемые аргументы, снова
  • аргументы ключевого слова
    • необязательный (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:[email protected](irb):24>

Более подробную информацию можно получить в официальной Документации по синтаксису Ruby.

Ответ 3

  • Аргументы с аргументами по умолчанию и аргументом splat должны быть сгруппированы вместе;
  • Аргумент Splat должен появиться после позиционных аргументов со значениями по умолчанию но до аргументов ключевого слова;
  • Аргументы ключевого слова должны появляться после позиционных аргументов и до двойной аргумент splat;
  • Двойной аргумент splat должен появляться последним, но перед аргументом блока.

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

Ответ 4

Аргументы ключевого слова - это просто необязательный хэш-аргумент, как я знаю.

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

совпадает с

def some_method(c, { a: 'first', b: 'second' }) // will not compile actually

Ruby просто интерпретирует последние аргументы с хэш-синтаксисом как хеш. Таким образом, первый пример не работает, потому что Ruby не может понять такой синтаксис (имеет синтаксис на первом месте).