NameError: undefined - есть правила синтаксического анализа для локальных переменных, измененных в Ruby 2.1.2?
Я получаю NameError: undefined local variable or method
с ruby 2.1.2
Как отмечено в этом вопросе, выражения типа:
bar if bar = true
вызывает ошибку локальной переменной undefined (при условии, что bar
не определена ранее), поскольку bar
считывается парсером до его назначения. И я считаю, что с этим выражением не было никакой разницы:
bar if bar = false
Разница между ними заключается в том, оценивается ли основное тело или нет, но не имеет значения, если локальная переменная undefined сразу же вызывает ошибку перед оценкой условия.
Но когда я запускаю второй код на Ruby 2.1.2, он не вызывает ошибку. Так было раньше? Если да, то в чем состояла дискуссия по разбору? Если нет, изменилась ли спецификация Ruby? Есть ли какая-то ссылка на это? Что он сделал в 1.8.7, 1.9.3 и т.д.?
Ответы
Ответ 1
Да, это изменилось в ruby 2.1.2
В 1.8.7
, 1.9.3
, 2.0.0
и даже 2.1.1
я получаю 2 предупреждения и никаких ошибок:
2.0.0-p247 :007 > bar if bar = false
(irb):7: warning: found = in conditional, should be ==
=> nil
2.0.0-p247 :008 > bar if bar = true
(irb):8: warning: found = in conditional, should be ==
=> true
тогда как в версии 2.1.2
вы упомянули, что я получаю 2 предупреждения и 1 NameError
.
2.1.2 :001 > bar if bar = true
(irb):1: warning: found = in conditional, should be ==
NameError: undefined local variable or method `bar' for main:Object
from (irb):1
from /home/durrantm/.rvm/rubies/ruby-2.1.2/bin/irb:11:in `<main>'
2.1.2 :002 > bar if bar = false
(irb):2: warning: found = in conditional, should be ==
=> nil
Это на моем Ubuntu 14
Ответ 2
Нет никакой разницы в отношении того, определяется ли bar
или нет. В обоих случаях bar
undefined в теле. Однако в последнем случае тело никогда не оценивается, поэтому оно не имеет значения. Вы никогда не разрешаете имя bar
, поэтому вы никогда не получите ошибку при разрешении имени.
Локальные переменные определяются при анализе присваивания.
Они инициализируются при выполнении задания.
Это отлично подходит для того, чтобы переменная была унициализирована. В этом случае он будет оценивать только nil
:
if false
bar = 42
end
bar
# => nil
Однако, если переменная undefined, тогда Ruby не знает, является ли голое слово локальной переменной или бесполезным безрецептурным сообщением отправить:
foo
# NameError: undefined local variable or method `foo'
# ^^^^^^^^^
# Ruby doesn't know whether it a variable or a message send
Сравните с:
foo()
# NoMethodError: undefined method `foo'
# ^^^^^^^^^^^^^
self.foo
# NoMethodError: undefined method `foo'
# ^^^^^^^^^^^^^
Теперь все вместе:
foo()
# NoMethodError: undefined method `foo'
self.foo
# NoMethodError: undefined method `foo'
foo
# NameError: undefined local variable or method `foo'
if false
foo = 42
end
foo
# => nil
foo = :fortytwo
foo
# => :fortytwo
Проблема в этом конкретном случае заключается в том, что порядок, в котором выражения анализируются (и, следовательно, порядок, в котором определены переменные) не соответствует порядку, в котором выполняются выражения.
Сначала выполняется назначение, которое позволит предположить, что bar
будет определен в теле. Но это не так, потому что тело сначала анализировалось, и поэтому я не знаю, был ли этот метод или переменная node вставлена в дерево синтаксиса до того, как назначение было когда-либо замечено.
Однако, если этот node никогда не интерпретируется, то есть условие ложно, тогда ничего плохого не произойдет.
Ответ 3
Мой ответ основан на Ruby 2.1.2.
Добавление с помощью @Jörg W Mittag answer.
Другой часто путающий случай заключается в использовании модификатора if
:
p a if a = 0.zero? # => NameError: undefined local variable or method `a'
Вместо того, чтобы печатать "true", вы получаете локальную переменную NameError, "undefined или метод" a ". Поскольку Ruby анализирует голый слева от if, если он еще не видел присваивания, предполагается, что вы хотите вызвать метод. Затем Ruby видит назначение a и предположим, что вы ссылаетесь на локальный метод.
Путаница происходит из выполнения выражения out-of-order
выражения. Сначала назначается локальная переменная, затем вы пытаетесь вызвать несуществующий метод.
Основываясь на приведенном выше объяснении -
bar if bar = false
просто возвращает nil
, поскольку выражение было оценено как false, тело кода, связанного с модификатором if
, не будет выполнено. nil
возвращается любым блоком в Ruby по умолчанию, когда нет явного значения по умолчанию.