Почему следующий код не выдает ошибку?
if false
x = 0
end
x #=> nil
Тогда как следующее выдает ошибку:
y # NameError: undefined local variable or method 'y' for main:Object
То же самое происходит с инструкциями, unless
& case
.
Почему следующий код не выдает ошибку?
if false
x = 0
end
x #=> nil
Тогда как следующее выдает ошибку:
y # NameError: undefined local variable or method 'y' for main:Object
То же самое происходит с инструкциями, unless
& case
.
Это из-за того, как работает анализатор Ruby. Переменные определяются синтаксическим анализатором, который проходит через код по строкам, независимо от того, будет ли он фактически выполнен.
Когда анализатор видит x =
, он определяет локальную переменную x
(со значением nil
) впредь в текущей области. Поскольку if
/unless
/case
/for
/while
не создает новую область, x
определяется и доступен вне кода. И поскольку внутренний блок никогда не оценивается как условное значение false, x
не назначается (и, следовательно, nil
).
Здесь приведен пример:
defined?(x) and x = 0
x #=> nil
Обратите внимание, что это довольно высокоуровневый обзор того, что происходит, и не обязательно точно, как работает парсер.
Это связано с причудой правил охвата Ruby.
В рубине неподдерживаемая переменная x
, появляющаяся сама по себе, может быть либо локальной переменной, либо вызовом метода - грамматика не может определить, что. Это до анализатора, чтобы понять это, поскольку он решает локальные ссылки переменных. Правило простое: если присваивание переменной с тем же именем уже было замечено в локальной области, то эта ссылка является локальной переменной, а ссылка привязана к этой локальной переменной. В противном случае это вызов метода, и он будет выглядеть как таковой во время выполнения.
Локальные ссылки на переменные в Ruby оптимизированы для поиска в массиве (каждой локальной переменной присваивается "слот", а привязанные ссылки на локальную переменную, генерируемые анализатором, преобразуются в ссылки на слоты). Массив инициализируется всеми nil
:
/* initialize local variables */
for (i=0; i < local_size; i++) {
*sp++ = Qnil;
}
Таким образом, если вы ссылаетесь на локальную переменную, которая не была назначена, через связанную локальную ссылку (что может произойти только в случае пропущенного назначения над ссылкой в той же локальной области), вы получаете nil
.
Я думал, что ваш вопрос интересен, поэтому я попытался найти его и нашел следующее: Я не понимаю рубиновую локальную область
Правильный ответ, похоже, поставил Йорг.
Давайте посмотрим, что произойдет, когда вы попытаетесь получить доступ к переменной, которая не инициализирована:
NameError: undefined local variable or method `UNDECLAREDVAR' for main:Object
Исключение указывает, что невозможно оценить, является ли переменная или метод. Причина, по которой оно не вызывает то же исключение, состоит в том, что неинициализированные локальные переменные установлены на nil. Итак, puts x
- это хорошо, потому что интерпретатор знает, что x
является переменным, но неинициализированным, а не методом.