Что делает оператор double * (splat)

Вы видели функцию, объявленную как это?

def foo a, **b
  ...
end

Я понимаю, что один * - это оператор splat. Что означает **?

Ответ 1

Ruby 2.0 представил аргументы ключевого слова, а ** действует как *, но для аргументов ключевого слова. Он возвращает Hash с парами ключ/значение.

Для этого кода:

def foo(a, *b, **c)
  [a, b, c]
end

Вот демо:

> foo 10
=> [10, [], {}]
> foo 10, 20, 30
=> [10, [20, 30], {}]
> foo 10, 20, 30, d: 40, e: 50
=> [10, [20, 30], {:d=>40, :e=>50}]
> foo 10, d: 40, e: 50
=> [10, [], {:d=>40, :e=>50}]

Ответ 2

Это оператор double splat, доступный с Ruby 2.0.

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

def my_method(**options)
  puts options.inspect
end

my_method(key: "value")

Вышеприведенный код печатает {key:value} на консоли.

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

Пример в реальной жизни:

Например, в Rails метод cycle выглядит следующим образом:

def cycle(first_value, *values)
  options = values.extract_options!
  # ...
end

Этот метод можно вызвать следующим образом: cycle("red", "green", "blue", name: "colors").

Это довольно распространенный шаблон: вы принимаете список аргументов, а последний - хэш параметров, который может быть извлечен, например, с помощью ActiveSupport extract_options!.

В Ruby 2.0 вы можете упростить следующие методы:

def cycle(first_value, *values, **options)
  # Same code as above without further changes!
end

По общему признанию, это лишь незначительное улучшение, если вы уже используете ActiveSupport, но для простого Ruby код получает довольно много краткости.

Ответ 3

Кроме того, вы можете использовать его в стороне вызывающего абонента следующим образом:

def foo(opts); p opts end
bar = {a:1, b:2}

foo(bar, c: 3)
=> ArgumentError: wrong number of arguments (given 2, expected 1)

foo(**bar, c: 3)
=> {:a=>1, :b=>2, :c=>3}

Ответ 4

Для целых чисел ** означает показатель степени т.е. 2 ** 3 совпадает с 2 ^ 3