#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
метод notget_points
. Спасибо за ваш совет.