Всего новичков: переменные экземпляра в рубине? - PullRequest
13 голосов
/ 10 октября 2009

Простите за общий вопрос новичка, но почему @game_score всегда ноль?

#bowling.rb

class Bowling
  @game_score = 0
    def hit(pins)
        @game_score = @game_score + pins
    end

    def score
        @game_score
    end
end

Ответы [ 4 ]

37 голосов
/ 11 октября 2009

Давайте пройдемся по коду, ладно?

#bowling.rb

class Bowling
  @game_score = 0 # (1)

На данный момент (1) мы все еще внутри класса Bowling. Помните: классы - это просто объекты, как и любые другие. Итак, на данный момент вы присваиваете 0 переменной экземпляра @game_score объекта класса Bowling.

 def hit(pins)
  @game_score = @game_score + pins # (2)

Теперь (2) мы внутри экземпляра метода класса Bowling. Т.е .: это метод, который будет принадлежать экземпляру из Bowling. Итак, теперь переменная экземпляра @game_score принадлежит экземпляру класса Bowling, а не самому классу.

Поскольку эта переменная экземпляра никогда ни к чему не инициализируется, она будет иметь значение nil (в Ruby неинициализированные переменные всегда оцениваются как nil), так что это оценивается как @game_score = nil + pins, и так как nil не имеет #+ метода, это приведет к возникновению исключения NoMethodError.

 end
 def score
  @game_score # (3)

И здесь (3) мы снова внутри экземпляра метода класса Bowling. Это всегда будет иметь значение nil, по причине, которую я изложил выше: @game_score никогда не инициализируется, поэтому оно оценивается как nil.

 end
end

Мы можем использовать возможности отражения Руби, чтобы взглянуть на происходящее:

p Bowling.instance_variable_get(:@game_score) # => 0
b = Bowling.new
p b.instance_variable_get(:@game_score) # => nil

Теперь давайте добавим значение в переменную экземпляра:

b.instance_variable_set(:@game_score, 1)
p b.score # => 1
b.hit(3)
p b.score # => 4

Итак, мы видим, что все работает как надо, нам нужно только выяснить, как обеспечить инициализацию переменной экземпляра.

Для этого нам нужно написать метод инициализатора. Как ни странно, метод инициализатора на самом деле является частным экземпляром метода , который называется initialize. (Причина, по которой initialize является методом экземпляра, а не методом класса, на самом деле довольно проста. Ruby разбивает создание объекта на две фазы: выделение памяти и инициализация объекта. Распределение памяти выполняется методом class . называется alloc, а инициализация объекта выполняется с помощью экземпляра метода с именем initialize. (Программисты Objective-C признают это.) Причина, по которой alloc является методом класса, заключается просто в том, что на данный момент в выполнении еще нет экземпляра. И причина того, что initialize является методом экземпляра, заключается в том, что инициализация объекта, очевидно, выполняется для каждого объекта. Для удобства есть стандартный метод фабричного класса, называемый new, который вызывает оба alloc и initialize для вас.)

class Bowling
 def initialize
  @game_score = 0
 end
end

Давайте проверим это:

c = Bowling.new
p c.score # => 0
c.hit(2)
p c.score # => 2

Кстати: только несколько незначительных советов в стиле Ruby: отступ - 2 пробела, а не 1 табуляция. И ваш hit метод был бы более идиотски @game_score += pins.

16 голосов
/ 10 октября 2009

Потому что у вас нет

def initialize
  @game_score = 0
end

Назначение в определении класса не делает то, что, как вы думаете, делает, и когда вызывается hit, оно не может добавить к nil.

Если вы сейчас спросите , что случилось с @game_score? , хорошо, всегда помните Класс это объект и Объект это класс .

Это здорово, как классы Ruby имеют это "реальное" существование в стиле дзен. В Ruby нет точно названных классов, скорее, имена классов являются ссылками на объекты класса Class. Присваивая @game_score вне метода экземпляра, вы создали переменную экземпляра класса , атрибут объекта класса Bowling, который является экземпляром класса Class. Эти объекты, как правило, не очень полезны. (См. Главу 1, Рубиновый путь , Хэл Фултон.)

9 голосов
/ 10 октября 2009

@game_score определено там, называется переменная экземпляра класса , которая является переменной, определенной для объекта класса singleton:

class << Bowling
  attr_accessor :game_score
end

Bowling.game_score #=> 0

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

0 голосов
/ 10 октября 2009

@ game_score здесь никогда не получит нулевое значение - вам нужно поместить его в инициализировать, как в

def initialize @game_score = 0 конец

...