Распаковка аргументов ключевых слов (splat) в 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, о которой сообщалось несколько раз (например,здесь мной), но не было зафиксировано.
Я предполагаю, что с момента введения функции аргумента ключевого слова синтаксис double splat стал мутным, и это является косвенной причиной этой ошибки. Я слышал, что Matz рассматривает возможность введения нового синтаксиса в некоторой будущей версии Ruby для различения хэшей и аргументов ключевых слов.
[Edit:я видел ответ @sawa после завершения моего. Я был прав: это жук!]
что разные результаты получаются, когда литеральный пустой хэш дважды splatted и пустой хэш, который является значением переменной double-splatted, кажется мне prima facia доказательств, что это из-за ошибки в Ruby. Чтобы понять, почему ошибка может существовать, рассмотрим сначала причину передачи метода с двойным splatted хэшем.
Предположим, мы определяем способ с некоторыми аргументами ключевого слова:
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]
однако для использования хэша с двойным splatted в качестве аргумента хэш не может содержать ключи, которые не перечислены в качестве аргументов в определении метода.
def my_method(x, a:)
[x, a]
end
h = { a: 2, b: 3 }
my_method(1, **h)
#=> ArgumentError: unknown keyword: b
поэтому возникает вопрос: можно ли передать в качестве аргумента пустой хэш с двойным splatted, учитывая, что все ключи хэша (none) включены в качестве аргументов в определение метода (в этом случае это не будет иметь никакого эффекта)? Давай попробуем.
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, как было предложено OP. Это пустой хэш-литерал, с ним можно справиться раньше в коде Ruby, чем если бы это было значение переменной. Я предполагаю, что тот, кто написал предыдущий код, ответил "Да" на вопрос, который я поставил выше, и тот, кто написал последний код, ответил "Нет" или не рассмотрел случай пустого хэша в этот момент.
если эта теория ошибок не сбита, OP или кто-то другой должен сообщить об этом.
У меня такое чувство, что вы спотыкаетесь о особенность оператора splat по отношению к этой пустой хэш-структуре. Кажется, что разбрызгивание пустого встроенного хэша приводит к его исчезновению, но все остальное расширяется как аргумент.
на самом деле это может быть ошибка в Ruby, хотя это такой причудливый случай edge, я не очень удивлен.
код f
функция не принимает аргументы ключевого слова любого вида, поэтому, если достаточно энергичная попытка чтобы их это не выходит. Последние два примера, похоже, пытаются принудительно ввести пустой хэш в качестве буквального аргумента.