Подтвердить что ты не робот

Почему я могу ссылаться на переменную вне оператора if/except/case, который никогда не запускался?

Почему следующий код не выдает ошибку?

if false
  x = 0
end

x  #=> nil

Тогда как следующее выдает ошибку:

y  # NameError: undefined local variable or method 'y' for main:Object

То же самое происходит с инструкциями, unless & case.

4b9b3361

Ответ 1

Это из-за того, как работает анализатор Ruby. Переменные определяются синтаксическим анализатором, который проходит через код по строкам, независимо от того, будет ли он фактически выполнен.

Когда анализатор видит x =, он определяет локальную переменную x (со значением nil) впредь в текущей области. Поскольку if/unless/case/for/while не создает новую область, x определяется и доступен вне кода. И поскольку внутренний блок никогда не оценивается как условное значение false, x не назначается (и, следовательно, nil).

Здесь приведен пример:

defined?(x) and x = 0
x  #=> nil

Обратите внимание, что это довольно высокоуровневый обзор того, что происходит, и не обязательно точно, как работает парсер.

Ответ 2

Это связано с причудой правил охвата Ruby.

В рубине неподдерживаемая переменная x, появляющаяся сама по себе, может быть либо локальной переменной, либо вызовом метода - грамматика не может определить, что. Это до анализатора, чтобы понять это, поскольку он решает локальные ссылки переменных. Правило простое: если присваивание переменной с тем же именем уже было замечено в локальной области, то эта ссылка является локальной переменной, а ссылка привязана к этой локальной переменной. В противном случае это вызов метода, и он будет выглядеть как таковой во время выполнения.

Локальные ссылки на переменные в Ruby оптимизированы для поиска в массиве (каждой локальной переменной присваивается "слот", а привязанные ссылки на локальную переменную, генерируемые анализатором, преобразуются в ссылки на слоты). Массив инициализируется всеми nil:

/* initialize local variables */
for (i=0; i < local_size; i++) {
    *sp++ = Qnil;
}

Таким образом, если вы ссылаетесь на локальную переменную, которая не была назначена, через связанную локальную ссылку (что может произойти только в случае пропущенного назначения над ссылкой в ​​той же локальной области), вы получаете nil.

Ответ 3

Я думал, что ваш вопрос интересен, поэтому я попытался найти его и нашел следующее: Я не понимаю рубиновую локальную область

Правильный ответ, похоже, поставил Йорг.

Давайте посмотрим, что произойдет, когда вы попытаетесь получить доступ к переменной, которая не инициализирована:

NameError: undefined local variable or method `UNDECLAREDVAR' for main:Object

Исключение указывает, что невозможно оценить, является ли переменная или метод. Причина, по которой оно не вызывает то же исключение, состоит в том, что неинициализированные локальные переменные установлены на nil. Итак, puts x - это хорошо, потому что интерпретатор знает, что x является переменным, но неинициализированным, а не методом.