Распаковка (сплат) ключевых слов в Ruby

То, что происходит ниже, кажется мне немного странным.

def f(a, b)
  puts "#{a} :: #{b}"
end

f(*[1, 2], **{}) # prints "1 :: 2"

hash = {}
f(*[1, 2], **hash)
ArgumentError: wrong number of arguments (3 for 2)

f(*[1, 2], **Hash.new)
ArgumentError: wrong number of arguments (3 for 2)

Это функция оптимизации компилятора?

3 ответа

Решение

Это ошибка Ruby, о которой несколько раз сообщалось (например, здесь мной), но она не была исправлена.

Я предполагаю, что с тех пор, как была введена функция аргумента ключевого слова, синтаксис двойных сплатов стал мутным, и это является косвенной причиной этой ошибки. Я слышал, что Matz рассматривает возможность введения нового синтаксиса в будущей версии Ruby, чтобы различать хэши и аргументы ключевых слов.

[Редактировать: я видел ответ @sawa после завершения моего. Я был прав: это ошибка!]

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

Предположим, мы определили метод с некоторыми ключевыми аргументами:

def my_method(x, a: 'cat', b: 'dog')
  [x, a, b]
end

my_method(1)
  #=> [1, "cat", "dog"] 

Значения по умолчанию применяются для двух ключевых аргументов. Теперь попробуйте:

my_method(1, a: 2)
  #=> [1, 2, "dog"]

Теперь давайте воспользуемся двойным хэшем.

h = { a: 2, b: 3 }

my_method(1, **h)
 #=> [1, 2, 3] 

Это работает так же с необходимыми аргументами ключевых слов (Ruby 2.1+).

def my_method(x, a:, b:)
  [x, a, b]
end

my_method(1, **h)
  #=> [1, 2, 3]

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

def my_method(x, a:)
  [x, a]
end

h = { a: 2, b: 3 }

my_method(1, **h)
  #=> ArgumentError: unknown keyword: b

В связи с этим возникает вопрос: можно ли передать в качестве аргумента двойной хэш с пустым аргументом, учитывая, что все ключи хеша (нет) включены в качестве аргументов в определение метода (в каком случае это не будет иметь никакого эффекта)? Давай попробуем.

def my_method(x)
  [x]
end

my_method(1, **{})
  #=> [1]

Да!

h = {}
my_method(1, **h)
  #=> ArgumentError: wrong number of arguments (given 2, expected 1)

Нет!

Это бессмысленно. Если предположить, что это ошибка, как она могла возникнуть? Я подозреваю, что это может быть связано с оптимизацией Ruby, как это было предложено ОП. Если пустой хеш является литералом, с ним можно было справиться раньше в коде Ruby, чем если бы это было значение переменной. Я предполагаю, что тот, кто написал предыдущий код, ответил "да" на вопрос, который я поставил выше, а тот, кто написал последний код, ответил "нет" или не рассмотрел случай пустого хэша в тот момент.

Если эта теория ошибок не сбита, ОП или кто-то еще должен сообщить об этом.

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

На самом деле это может быть ошибкой в ​​Ruby, хотя это такой причудливый крайний случай, я не очень удивлен.

Ваш f Функция не принимает ключевые аргументы любого рода, поэтому, если будет предпринята достаточно энергичная попытка предоставить их, она потерпит неудачу. Последние два примера, кажется, пытаются использовать пустой хеш в качестве буквального аргумента.

Другие вопросы по тегам