Где и как указывается переменная _ (подчеркивание)?
Большинство из них знают о специальном значении _
в IRB как держателе для последнего значения возврата, но это не то, о чем я прошу здесь.
Вместо этого я спрашиваю о _
при использовании в качестве имени переменной в обычном-Ruby-коде. Здесь он, похоже, имеет особое поведение, сродни переменной "не волнует меня" (à la Prolog). Вот несколько полезных примеров, иллюстрирующих его уникальное поведение:
lambda { |x, x| 42 } # SyntaxError: duplicated argument name
lambda { |_, _| 42 }.call(4, 2) # => 42
lambda { |_, _| 42 }.call(_, _) # NameError: undefined local variable or method `_'
lambda { |_| _ + 1 }.call(42) # => 43
lambda { |_, _| _ }.call(4, 2) # 1.8.7: => 2
# 1.9.3: => 4
_ = 42
_ * 100 # => 4200
_, _ = 4, 2; _ # => 2
Все они были запущены в Ruby напрямую (с добавлением puts
) - не IRB - чтобы не противоречить его дополнительным функциям.
Все это результат моего собственного эксперимента, хотя я не могу найти никакой документации по этому поведению в любом месте (по общему признанию, это не самая простая вещь для поиска). В конечном счете, мне любопытно, как все это работает внутри, поэтому я могу лучше понять, что особенного в _
. Поэтому я запрашиваю ссылки на документацию и, желательно, исходный код Ruby (и, возможно, RubySpec), которые показывают, как ведет себя _
в Ruby.
Примечание: большая часть этого возникла из этой дискуссии с @Niklas B.суб >
Ответы
Ответ 1
В источнике есть некоторая специальная обработка для подавления ошибки "дублируемое имя аргумента". Сообщение об ошибке появляется только в shadowing_lvar_gen
внутри parse.y
, версия 1.9.3 выглядит следующим образом:
static ID
shadowing_lvar_gen(struct parser_params *parser, ID name)
{
if (idUScore == name) return name;
/* ... */
и idUScore
- определенный в id.c
следующим образом:
REGISTER_SYMID(idUScore, "_");
Вы увидите аналогичную специальную обработку в warn_unused_var
:
static void
warn_unused_var(struct parser_params *parser, struct local_vars *local)
{
/* ... */
for (i = 0; i < cnt; ++i) {
if (!v[i] || (u[i] & LVAR_USED)) continue;
if (idUScore == v[i]) continue;
rb_compile_warn(ruby_sourcefile, (int)u[i], "assigned but unused variable - %s", rb_id2name(v[i]));
}
}
Вы заметите, что предупреждение подавлено во второй строке цикла for
.
Единственная специальная обработка _
, которую я мог найти в источнике 1.9.3, приведен выше: ошибка с дублирующимся именем подавляется, и предупреждение о неиспользуемой переменной подавляется. Помимо этих двух вещей, _
- это просто старая переменная, как и любая другая. Я не знаю никакой документации о (незначительной) особой _
.
В Ruby 2.0 тест idUScore == v[i]
в warn_unused_var
заменяется вызовом is_private_local_id
:
if (is_private_local_id(v[i])) continue;
rb_warn4S(ruby_sourcefile, (int)u[i], "assigned but unused variable - %s", rb_id2name(v[i]));
и is_private_local_id
подавляет предупреждения для переменных, начинающихся с _
:
if (name == idUScore) return 1;
/* ... */
return RSTRING_PTR(s)[0] == '_';
а не только _
. Таким образом, 2.0 немного ослабляет ситуацию.
Ответ 2
_
является допустимым идентификатором. Идентификаторы не могут просто содержать символы подчеркивания, они также могут быть подчеркиванием.
_ = o = Object.new
_.object_id == o.object_id
# => true
Вы также можете использовать его как имена методов:
def o._; :_ end
o._
# => :_
Конечно, это не совсем читаемое имя, и он не передает никакой информации читателю о том, что относится к переменной или о том, что делает этот метод.
IRB
, в частности, устанавливает _
значение последнего выражения:
$ irb
> 'asd'
# => "asd"
> _
# => "asd"
Как в исходном коде, он просто устанавливает _
в последнее значение:
@workspace.evaluate self, "_ = IRB.CurrentContext.last_value"
Проанализировал некоторые репозитории. Вот что я нашел:
В последних строках файла id.c
есть вызов:
REGISTER_SYMID(idUScore, "_");
grep
источник для idUScore
дал мне два, казалось бы, релевантных результата:
shadowing_lvar_gen
представляется механизмом, посредством которого формальный параметр блока заменяет переменную с тем же именем, которая существует в другой области. Это функция, которая, по-видимому, вызывает "дублированное имя аргумента" SyntaxError
и предупреждение "затенение внешней локальной переменной".
После grep
источника для shadowing_lvar_gen
я нашел следующее в списке изменений для Ruby 1.9.3:
Вт 11 дек 01:21:21 2007 Юкихиро Мацумото
- parse.y(shadowing_lvar_gen): нет дублирующейся ошибки для "_".
Скорее всего, это будет начало этой строки:
if (idUScore == name) return name;
Из этого выведем, что в такой ситуации, как proc { |_, _| :x }.call :a, :b
, одна переменная _
просто затеняет другую.
Здесь рассматриваемая сделка. Он в основном ввел эти две строки:
if (!uscore) uscore = rb_intern("_");
if (uscore == name) return;
От времени, когда idUScore
даже не существовало, по-видимому.