Распаковка (сплат) ключевых слов в 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
Функция не принимает ключевые аргументы любого рода, поэтому, если будет предпринята достаточно энергичная попытка предоставить их, она потерпит неудачу. Последние два примера, кажется, пытаются использовать пустой хеш в качестве буквального аргумента.