初めに!
- 学習した内容をアウトプットすることで学習の定着に繋げます。
前提条件
コード
class Drink attr_reader :name, :price def initialize(name, price) @name = name @price = price end end class VendingMachine attr_accessor :stocks attr_reader :prices, :sales_amount def initialize pepsi = Drink.new('pepsi', 150) water = Drink.new('water', 120) redble = Drink.new('redble', 200) stock_table = { pepsi => 5, water => 5, redble => 5 } prices = { pepsi => pepsi.price, water => water.price, redble => redble.price } @stock_table = stock_table @prices = prices end def recognize(drink) @stock_table.key?(drink) end end pepsi = Drink.new('pepsi', 150) p pepsi.name vm = VendingMachine.new p vm.recognize(pepsi)
出力
recognize.rb:25:in `recognize': undefined local variable or method `drink' for #<VendingMachine:0x0000000105798550 @stock_table={#<Drink:0x00000001057983c0 @name="pepsi", @price=150>=>5, #<Drink:0x00000001057982f8 @name="water", @price=120>=>5, #<Drink:0x0000000105798280 @name="redble", @price=200>=>5}, @prices={#<Drink:0x00000001057983c0 @name="pepsi", @price=150>=>150, #<Drink:0x00000001057982f8 @name="water", @price=120>=>120, #<Drink:0x0000000105798280 @name="redble", @price=200>=>200}> (NameError) @stock_table.key?(drink) ^^^^^ Did you mean? dirnk from recognize.rb:31:in `<main>'
key?メソッドで@stock_tableの中のキーにdrinkオブジェクトが生成されているか確認したがキーとして認識してもらえていない。
ただしハッシュのキーと値のペアの表示形式が入っている。今回キーとして認識して欲しいのが@nameと@priceの値。
考えたこと
メモリアルアドレスはキーとして扱われない?
ハッシュのキーについての理解が甘い?
そもそもキーにオブジェクトを格納することができない?
ハッシュについて
ハッシュテーブルのクラス。
ハッシュは任意のオブジェクトを(キー)から値を関連付けて格納する。
ハッシュのキーは任意のものを格納できるがhashメソッド、eql?メソッドが適切に定義されている必要がある。
hashメソッドはキーにハッシュ値を与えるメソッド
eql?メソッドはキーの同一性を判定する。
== メソッドと eql? メソッド、および hash メソッドをオーバーライドする
- 同じ属性を持つDrinkオブジェクトを同一のものみなすように改善していく。 Drinkクラス
class Drink attr_reader :name, :price def initialize(name, price) @name = name @price = price end def hash [name, price].hash end def ==(other) other.is_a?(Drink) && name == other.name && price == other.price end def eql?(other) self == other end end pepsi = Drink.new('pepsi', 150) vm = VendingMachine.new p vm.recognize(pepsi)
hashメソッドでは[name,price]のハッシュ値を求めている。キーに格納する値がこの2つであるから。
==メソッドはotherがDrinkクラスに属しているか?そしてname属性とprice属性が等しいか示している
eql?メソッドは==メソッドを使用して2つのオブジェクトが等しいか確認する。hashクラスでクラスでキーの比較で使用される。
修正後のコード
コード
class Drink attr_reader :name, :price def initialize(name, price) @name = name @price = price end def hash [name, price].hash end def ==(other) other.is_a?(Drink) && name == other.name && price == other.price end def eql?(other) self == other end end class VendingMachine attr_accessor :stocks attr_reader :prices, :sales_amount def initialize pepsi = Drink.new('pepsi', 150) water = Drink.new('water', 120) redble = Drink.new('redble', 200) stock_table = { pepsi => 5, water => 5, redble => 5 } prices = { pepsi => pepsi.price, water => water.price, redble => redble.price } @stock_table = stock_table @prices = prices end def recognize(drink) @stock_table.key?(drink) end end pepsi = Drink.new('pepsi', 150) vm = VendingMachine.new p vm.recognize(pepsi)
出力
true
ハッシュへキーを追加する
* stock_table配列にpepsiオブジェクトをキーとして格納される時にhashメソッドが呼び出されてオブジェクト値を算出する。
- hashメソッドは[name,price].hashを返す。
stock = { pepsi => 5 }
キーを呼び出す
recognizeオブジェクトを呼び出して@stock_tableの中にキーが存在しているか確認する。
@stock_table.key?を呼び出すとdrink.hashが呼び出され、pepsi.hashと同じハッシュ値を取得する。
等価性の確認 (eql? メソッドの呼び出し)
同じハッシュ値のキーが見つかるとハッシュテーブルはeql?を呼び出してキーの等価性を判断する。
eql?メソッド内部では==メソッドを呼び出して同じクラスかどうか?name属性、price属性が同じかどうか判断を行う。