ハッシュに格納したキーが呼び出せない。

初めに!

  • 学習した内容をアウトプットすることで学習の定着に繋げます。

前提条件

  • 自動販売機にジュースを格納するというシステムを作成したい。

  • 初期設定としてペプシ、水、レッドブルを5本ずつ格納する。

  • 格納した変数からkey?メソッドでキーが存在しているか確認する。

コード

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属性が同じかどうか判断を行う。

結語

  • ハッシュのキーに格納するためにはハッシュ値が必要かつキーの同一性が必要。

  • オブジェクトのハッシュ値が違うと違うものと判断されてしまう。

  • hashメソッドとeql?メソッドは配列のキーに適応するオブジェクトは定義されている必要がある。

  • hashメソッドはオブジェクトからハッシュ値を算出するメソッド

  • eql?メソッドはオブジェクト同士のハッシュ値の等価性を判断するメソッド。2つのキーが等しいか判断する。

  • key?メソッドにて呼び出されるにはハッシュ値が必要となる。