Подтвердить что ты не робот

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

То же самое в 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

почему переменная экземпляра обрабатывается иначе, чем локальная и переменная класса?

4b9b3361

Ответ 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

почему переменная экземпляра обрабатывается иначе, чем локальная и переменная класса?

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

есть другие причины & hellip; не могут ли переменные класса так себя вести?

Я не знаю. Например, переменные очень удобны, поскольку, например, в отличие от Java, где переменные экземпляра объявляются в определении класса и поэтому всегда существуют для каждого экземпляра класса, в Ruby переменные экземпляра нигде не объявляются. Они просто магически spring в существование, как только они назначены. Поскольку переменные экземпляра не обязательно гарантированно существуют, писать методы, которые используют переменные экземпляра, были бы болью, если бы они бросали исключения.

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