В Ruby, почему после запуска irb foo.nil? выдает неопределенную ошибку, а @foo.nil? выдает «true», а @@wah.nil? снова выдает ошибку?

#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 переменные экземпляра нигде не объявлены. Они просто волшебным образом возникают, как только им назначают. Поскольку существование переменных экземпляра не обязательно гарантировано, написание методов, использующих переменные экземпляра, было бы затруднительным, если бы они вызывали исключения.

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