Почему эта переменная экземпляра не увеличивается?

#ruby

#ruby

Вопрос:

Этот короткий код работает, когда я использую переменную класса @@points вместо @points . Интересно, почему это происходит именно так? Кто-нибудь может мне объяснить? Похоже, что это @points всегда nil .

 class Game

  @points = 0

  def start

    until @points == 10
      puts "Guess number between 0 and 10:" 
      num = gets.chomp.to_i
      break if @points == 0
      guess_number(num)
      puts "Your score is: #{@points}"

    end

  end

  def guess_number(num)
    @points  = 1 if num == rand(0..10)
  end

end

game = Game.new
game.start
  

Комментарии:

1. Крошечная точка: gets.chomp.to_i часто записывается без chomp : gets.to_i . Это потому, что "123".to_i , "123n".to_i и "123X456abc".to_i все возвращают 123 . Смотрите Строку#to_i , которая включает в себя: «Посторонние символы после конца допустимого числа игнорируются «..

Ответ №1:

Потому что @points это переменная экземпляра класса, и для доступа к ней из области видимости метода экземпляра вам придется либо выполнить

 self.class.instance_variable_get(:@points)
  

или определите attr_accessor в Game singleton_class

 class Game; class << self; attr_accessor :points; end; end
  

и тогда вы могли бы сделать

 self.class.points
  

Но ни одно из этих действий не является тем, чего вы действительно хотите.

код работает, когда я использую переменную класса @@points вместо @points

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

Похоже, что @points всегда nil

Это происходит всегда nil , потому что вы никогда не определяли переменную экземпляра @points , но, как уже было сказано, переменную экземпляра класса.

Итак, эти три вещи разные (вы могли бы прочитать что-нибудь об области видимости Ruby — не смешивать с областями AR):

  • переменная класса
  • переменная экземпляра класса
  • переменная экземпляра

Для решения этой проблемы есть много способов, но если вы хотите сохранить ее на уровне экземпляра, оберните @points в метод:

 def points
  @points ||= 0
end
  

А затем используйте ее как points — теперь она будет работать так, как вы ожидаете.

Комментарии:

1. Если бы OP не хотел выставлять точки за пределами класса, не было бы проще реализовать конструктор, который устанавливает @points = 0 ?

2. @pjs как я уже сказал, есть несколько способов сделать это, так что выбор за OP, но, конечно, это один из них 🙂

3. Может ввести в заблуждение утверждение, что значение переменной экземпляра класса не может быть получено в области видимости экземпляра, поскольку это можно сделать с помощью self.class.instance_variable_get(:@points) (как вы хорошо знаете).

Ответ №2:

Спасибо за ответ Андрея Дейнеко. Я придумал такое решение, чтобы использовать переменную экземпляра здесь.

 class Game

  def initialize
    @points = 0
  end

  def start

    until points == 10
      puts "Guess number between 0 and 10:" 
      num = gets.chomp.to_i
      break if points == 10
      guess_number(num)
      puts "Your score is: #{points}"

    end

  end

  private

  def guess_number(num)
    if num == rand(0..10)
      increment_points
    end
  end

  def points
    @points
  end

  def increment_points
    @points  = 1
  end

end

game = Game.new
game.start
  

Комментарии:

1. Опасность этого подхода заключается в том, что пользователь, который зайдет в ваш класс, обнаружит, что он может вызывать increment_points напрямую, не играя в игру. Если вы хотите использовать переменную экземпляра, добавьте initialize метод к классу, в котором @points при создании Game.new экземпляра инициализируется значение 0.

2. Кажется, это хорошая идея. Я отредактировал свой сервер ответов, а также сделал некоторые методы закрытыми.

3. Вы также нарушаете канонические стандарты Ruby, называя средство получения чем-то иным, чем то, как его назвал бы attr_reader. Просто нужно кое-что учесть.

4. Я изменил ее на просто points метод not get_points . Спасибо за ваш совет.