Почему Ruby, кажется, поднимает объявления переменных изнутри case-case, даже если этот путь кода не выполняется?
Определим функцию foo:
def foo(s)
case s
when'foo'
x = 3
puts x.inspect
when 'bar'
y = 4
puts y.inspect
end
puts x.inspect
puts y.inspect
end
Затем мы называем это следующим образом:
1.9.3p194 :017 > foo('foo')
in foo scope
3
in outer scope
3
nil
=> nil
1.9.3p194 :018 > foo('bar')
in bar scope
3
in outer scope
nil
3
=> nil
Почему функция не выдает ошибку о незарегистрированной локальной переменной в любом случае? В первом случае переменная y
кажется, что она не должна существовать, поэтому вы не можете называть ее inspect
во внешней области; то же самое для x
во втором случае.
Вот еще один пример:
def test1
x = 5 if false
puts x.inspect
end
def test2
puts x.inspect
end
И затем:
1.9.3p194 :028 > test1
nil
=> nil
1.9.3p194 :029 > test2
NameError: undefined local variable or method `x' for main:Object
Что здесь происходит? Кажется, что Ruby поднимает объявление переменной во внешнюю область, но я не знал, что это что-то делает Ruby. (Поиск "рубинового подъема" только показывает результаты подъема JavaScript.)
Ответы
Ответ 1
Когда анализатор Ruby видит идентификатор последовательности, знак равенства, значение, как в этом выражении
x = 1
выделяет пространство для локальной переменной с именем x
. Создание переменная - не присвоение ей значения, а внутренняя создание переменной - всегда происходит в результате такого рода выражение, даже если код не выполнен! Рассмотрим этот пример:
if false
x = 1
end
p x # Output: nil
p y # Fatal Error: y is unknown
Назначение x
не выполняется, потому что его завернутый в неудачный условный тест. Но анализатор Ruby видит последовательность x = 1
, из который выводит, что программа включает локальную переменную x
. Парсер не заботится о том, когда x присваивается значение. Его работа просто для очистки кода для локальных переменных, для которых необходимо пространство будет выделено. Результат состоит в том, что x обитает в странном виде переменной limbo. Он был создан и инициализирован до nil
. В этом отношении он отличается от переменной, которая не существует при все; как вы можете видеть в примере, изучение x
дает вам значение nil
, тогда как при попытке проверить несуществующую переменную y
результаты в фатальной ошибке. Но хотя x
существует, он не играл никакой роли в программа. Он существует только как артефакт процесса синтаксического анализа.
Хорошо обоснованный рубист глава 6.1.2
Ответ 2
Анализатор рубина проходит через каждую строку и устанавливает нулевое значение variable =
. Фактически выполненный код не имеет значения.
См. Почему я могу ссылаться на переменную за пределами оператора if/except/case, который никогда не запускался?