Где и как указана переменная _ (подчеркивание)? - PullRequest
75 голосов
/ 05 марта 2012

Большинство знает о специальном значении _ в IRB как держателя последнего возвращаемого значения, но это не , о чем я здесь спрашиваю.

Вместо этого я спрашиваю о _ при использовании в качестве имени переменной в обычном старом Ruby-коде. Здесь он, кажется, имеет особое поведение, похожее на переменную «все равно» (а-ля Пролог ). Вот несколько полезных примеров, иллюстрирующих его уникальное поведение:

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 s) - не IRB - чтобы избежать конфликта с его дополнительными функциями.

Это все результат моих собственных экспериментов, так как я нигде не могу найти никакой документации по этому поведению (по общему признанию, это не самая легкая вещь для поиска). В конечном счете, мне любопытно, как все это работает внутри, так что я могу лучше понять, что особенного в _. Поэтому я прошу ссылки на документацию и, предпочтительно, на исходный код Ruby (и, возможно, RubySpec ), который показывает, как _ ведет себя в Ruby.

Примечание: большая часть этого возникла из этого обсуждения с @ Niklas B.

Ответы [ 2 ]

51 голосов
/ 05 марта 2012

Существует некоторая специальная обработка в источнике, чтобы подавить ошибку «дубликата имени аргумента».Сообщение об ошибке появляется только в 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 немного ослабляет вещи.

22 голосов
/ 05 марта 2012

_ является действительным идентификатором.Идентификаторы не могут содержать только подчеркивания, они также могут быть подчеркиванием.

_ = 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, по-видимому, даже не существовало.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...