Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rubyハッシュキーとしてのオブジェクト

Ruby 3x3の取り組みをフォローしている場合は、おそらくOptcarrotについて聞いたことがあるでしょう。これは純粋なRubyで書かれたNESエミュレーターです。

私は最近、Optcarrotのソースを調べていましたが、興味深い詳細が1つ浮き彫りになりました。これは、見過ごされがちですが非常に便利なRubyのハッシュの機能を多用します。これは、任意のオブジェクトをハッシュキーとして使用する機能です。

コンテキスト:NESメモリマッピング

高レベルのプログラマーとして、私たちはメモリをRAMと考える傾向があります。しかし、下位レベルでは、「メモリ」には他にも多くの用途があります。

「メモリ」の読み取りと書き込みは、NESのCPUがGPU、コントロールパッド、およびカートリッジ上の特別な電子機器と通信する方法です。使用するアドレスに応じて、write_to_memory メソッド呼び出しは、ジョイスティックをリセットしたり、VRAMを交換したり、サウンドを再生したりする可能性があります。

これをRubyでどのように実装しますか?

Optcarrotは、2つのMethodを保存することでそれを行います 65536アドレスごとのオブジェクト。 1つはゲッターで、もう1つはセッターです。次のようになります:

@getter_methods[0x0001] = @ram.method(:[])
@setter_methods[0x0001] = @ram.method(:[]=)

問題:オブジェクトの重複

Object#methodの使用に関する問題 このように、それは多くの個別のMethodを作成するということです 同一のオブジェクト。

これは、object_idを見るとわかります。 :

> a = []
> a.method(:[]=).object_id
=> 70142391223600
> a.method(:[]=).object_id
=> 70142391912420

2つのMethod オブジェクトのobject_idは異なります 値なので、同じことをしていても、それらは異なるオブジェクトです。

通常、いくつかの余分なMethodは気にしないかもしれません オブジェクトですが、この場合は何千ものオブジェクトを扱っています。

解決策:ハッシュによるメモ化

Optcarrotは、重複するMethodを回避します 見落としがちな非常に単純なトリックでのオブジェクトの問題。

ハッシュを使用してメモ化し、重複排除します。以下の簡略化されたコードは、この手法を示しています。

def initialize
  @setter_methods = []
  @setter_cache = {}
  ...
end

def add_setter(address, setter)
  # Doesn't store duplicates
  @setter_cache[setter] ||= setter

  # Use the deduped version
  @setter_methods[address] = @setter_cache[setter]  
end

これは、Hashが機能するために機能します キーとしてどのようなオブジェクトを指定してもかまいません。

これが紛らわしい場合は、文字列を使用してIRBで試してください:

> cache = {}
> cache["foo"] ||= "bar" 
=> "bar"
cache["foo"] ||= "baz"
=> "bar"

ここで、Rubyでは、文字列はクラスStringのインスタンスであると考えてください。 。 Rubyが文字列をハッシュキーとして使用するために使用したメカニズムは、基本的にMethodを格納するために使用したメカニズムと同じです。 物体。

ハッシュが平等を計算する方法

文字列以外のオブジェクトをハッシュキーとして使用すると、Hashはどのように行われるのかという疑問が生じます。 2つのオブジェクトが等しいかどうか知っていますか?

答えは、Object#hashを使用しているということです 方法。このメソッドはオブジェクトを調べ、再帰的にハッシュを生成します。次のようになります:

> a.method(:[]=).hash
=> 929915641391564853

同一のオブジェクトは同一のハッシュ値を生成するため、同等性のテストとして使用できます。

a.hash == b.hash

興味深いことに、これはeql?で使用されているのと同じアプローチです。 方法:

a.eql?(b)

これはMethodで機能します この例のオブジェクト:

> a.method(:[]=).hash == a.method(:[]=).hash
=> true
結論

Ruby Web開発パターンに慣れてきたので、optcarrotソースを見て、リアルタイムの非Webアプリがさまざまなパターンをどのように使用しているかを確認するのは非常に興味深いことでした。 Webアプリでは、65536要素の配列を作成することはないと思いますが、ここでは、「デスクトップ」アプリのセットアップの一部として、それは非常に理にかなっています。

ご質問やご意見がございましたら、starr@honeybadger.ioまたはTwitterの@StarrHorneまでご連絡ください。


  1. Ruby内部:Rubyオブジェクトのメモリレイアウトの調査

    Ruby内部のクイックツアーをご希望ですか? その後、あなたは御馳走になります。 なぜなら … Rubyオブジェクトがメモリ内にどのように配置されるか、そして内部データ構造を操作していくつかのクールなことを行う方法を一緒に探求します。 シートベルトを締めて、Rubyインタープリターの奥深くへの旅の準備をしてください! アレイのメモリレイアウト 配列を作成するとき、Rubyはそれをシステムメモリと少しのメタデータでバックアップする必要があります。 メタデータに含まれるもの : 配列サイズ(アイテム数) アレイ容量 クラス オブジェクトのステータス(凍結されているかどうか) データが

  2. Ruby Freezeメソッド–オブジェクトの可変性を理解する

    オブジェクトが可変であるとはどういう意味ですか? 派手な言葉で混乱させないでください。「可変性 」は、オブジェクトの内部状態を変更できることを意味します。これは、凍結されたオブジェクトを除く、すべてのオブジェクトのデフォルトです。 、または特別なオブジェクトのリストの一部であるもの。 つまり、Rubyのすべてのオブジェクトが変更可能というわけではありません! 例 : 数字や記号、さらにはtrueには意味がありません またはfalse (オブジェクトでもあります)変更します。 数字の1は常に1になります。 ただし、他のオブジェクト、特に配列オブジェクトやハッシュオブジェクトなどのデー