#ruby #instance-variables #local-variables #class-variables
#ruby #переменные экземпляра #локальные переменные #переменные класса
Вопрос:
То же самое в Ruby 1.8.7 и 1.9.2:
$ irb
ruby-1.8.7-p302 > foo.nil?
NameError: undefined local variable or method `foo' for #<Object:0x3794c>
from (irb):1
ruby-1.8.7-p302 > @bar.nil?
=> true
ruby-1.8.7-p302 > @@wah.nil?
NameError: uninitialized class variable @@wah in Object
from (irb):3
почему переменная экземпляра обрабатывается иначе, чем локальная переменная и переменная класса?
Ответ №1:
В Ruby большинство неинициализированных или даже несуществующих переменных оцениваются как nil
. Это верно для локальных переменных, переменных экземпляра и глобальных переменных:
defined? foo #=> nil
local_variables #=> []
if false
foo = 42
end
defined? foo #=> 'local-variable'
local_variables #=> [:foo]
foo #=> nil
foo.nil? #=> true
defined? @bar #=> nil
instance_variables #=> []
@bar #=> nil
@bar.nil? #=> true
# warning: instance variable @bar not initialized
defined? $baz #=> nil
$baz #=> nil
# warning: global variable `$baz' not initialized
$baz.nil? #=> true
# warning: global variable `$baz' not initialized
Однако это неверно для переменных и констант иерархии классов:
defined? @@wah #=> nil
@@wah
# NameError: uninitialized class variable @@wah in Object
defined? QUUX #=> nil
QUUX
# NameError: uninitialized constant Object::QUUX
Это отвлекающий маневр:
defined? fnord #=> nil
local_variables #=> []
fnord
# NameError: undefined local variable or method `fnord' for main:Object
Причина, по которой вы получаете здесь ошибку, не в том, что неинициализированные локальные переменные не вычисляются до nil
, а в том, что fnord
это неоднозначно: это может быть либо сообщение без аргументов, отправленное получателю по умолчанию (т. Е. эквивалентно self.fnord()
), либо fnord
доступ к локальной переменной, не имеющей значения для, ,.
Чтобы устранить неоднозначность, вам нужно добавить получателя или список аргументов (даже если он пустой), чтобы сообщить Ruby, что это отправленное сообщение:
self.fnord
# NoMethodError: undefined method `fnord' for main:Object
fnord()
# NoMethodError: undefined method `fnord' for main:Object
или убедитесь, что анализатор (не вычислитель) анализирует (не выполняет) присваивание перед использованием, чтобы сообщить Ruby, что это локальная переменная:
if false
fnord = 42
end
fnord #=> nil
почему переменная экземпляра обрабатывается иначе, чем локальная переменная и переменная класса?
На самом деле это не так. Обрабатывается так же, как локальная переменная. Переменная иерархии классов ведет себя по-разному, локальные переменные, переменные экземпляра и глобальные переменные ведут себя одинаково.
есть ли другие причины … переменные класса не могут вести себя подобным образом?
Я не знаю. Для переменных экземпляра это очень удобно, поскольку в отличие, например, от Java, где переменные экземпляра объявлены в определении класса и, следовательно, всегда существуют для каждого экземпляра класса, в Ruby переменные экземпляра нигде не объявлены. Они просто волшебным образом возникают, как только им назначают. Поскольку существование переменных экземпляра не обязательно гарантировано, написание методов, использующих переменные экземпляра, было бы затруднительным, если бы они вызывали исключения.
Почему переменные иерархии классов отличаются, я понятия не имею. Возможно, это потому, что их все равно никто не использует, или потому, что они обычно инициализируются в теле класса и к ним просто не обращаются, когда они не инициализированы.