まったくの初心者の質問で申し訳ありませんが、@game_score が常に nil なのはなぜですか?
#bowling.rb
class Bowling
@game_score = 0
def hit(pins)
@game_score = @game_score + pins
end
def score
@game_score
end
end
まったくの初心者の質問で申し訳ありませんが、@game_score が常に nil なのはなぜですか?
#bowling.rb
class Bowling
@game_score = 0
def hit(pins)
@game_score = @game_score + pins
end
def score
@game_score
end
end
コードを見ていきましょう。
#bowling.rb
class Bowling
@game_score = 0 # (1)
この時点 (1) では、まだクラス Bowling内にいます。覚えておいてください: クラスは他のオブジェクトと同じように単なるオブジェクトです。したがって、この時点でクラス object0のインスタンス変数に代入しています。@game_score Bowling
def hit(pins)
@game_score = @game_score + pins # (2)
(2) では、クラスのインスタンス メソッドの内部にいます。つまり、これは のインスタンスBowlingに属するメソッドです。したがって、インスタンス変数は、クラス自体ではなく、クラスのインスタンスに属します。Bowling@game_scoreBowling
このインスタンス変数は何にも初期化されないため(nil Ruby では、初期化されていない変数は常に に評価されますnil)、これは に評価され@game_score = nil + pins、メソッドnilがないため、例外が発生します。#+NoMethodError
end
def score
@game_score # (3)
ここ (3) では、再びクラスのインスタンス メソッド内にいます。これは、上で概説した理由により、Bowling常に と評価されます。初期化されないため、 と評価されます。nil@game_scorenil
end
end
Ruby のリフレクション機能を使用して、何が起こっているかを確認できます。
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はオブジェクト生成をメモリ割り当てとオブジェクト初期化の2段階に分けています。メモリ割り当ては呼び出されたクラスメソッドによって行われalloc、オブジェクトの初期化はインスタンスによって行われます。 (Objective-C プログラマーはこれに気付くでしょう。) なぜクラスメソッドなのinitializeかという理由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 スタイルのヒント: インデントは 1 つのタブではなく 2 つのスペースです。そして、あなたのhit方法はより慣用的に@game_score += pins.
あなたが持っていないので
def initialize
@game_score = 0
end
クラス定義の割り当ては、あなたが思っていることをしていません。hit呼び出されたときに、に追加することはできませんnil。
あなたが今、何が起こったのか尋ねたら@game_score?、ええと、 Class は objectであり、Object は classであることを常に覚えておいてください。
Ruby クラスがこの禅のような「本物の」存在を持っている方法は、とてもクールです。Ruby には名前付きのクラスが正確に存在するわけではありません。むしろ、クラス名は class のオブジェクトへの参照ですClass。@game_scoreインスタンス メソッドの外側に代入することにより、クラス オブジェクトの属性であるクラス インスタンス変数Bowlingを作成しました。これは、クラスのインスタンスですClass。一般に、これらのオブジェクトはあまり役に立ちません。(Chapter 1, The Ruby Way、Hal Fulton を参照してください。)
@game_scoreそこで定義されているのは、クラスインスタンス変数と呼ばれるものです。これは、シングルトンクラスオブジェクト用に定義された変数です。
class << Bowling
attr_accessor :game_score
end
Bowling.game_score #=> 0
これは、インスタンスオブジェクトに対して定義された通常のインスタンス変数とは異なることがわかるためです。
@game_scoreがここでゼロの値を取得することはありません-次のように、initialize内に配置する必要があります
def initialize @game_score = 0 end