Где и как указывается переменная _ (подчеркивание)?

Большинство из них знают о специальном значении _ в 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 даже не существовало, по-видимому.