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

高度なRubyハッシュ手法

Ruby開発者がハッシュを使用するのと同じくらい何かを使用する場合、それをすべて見たと考えるのは簡単です。

しかし、私はここにいるので、謙虚なルビーハッシュにはいくつかのトリックがあります。ばかげたKey-Valueシステムではなく、Hashオブジェクトを使用すると、非常に興味深く洗練された処理を実行できます。

任意のオブジェクトをハッシュキーにすることができます

先に進む前に、明らかではないかもしれないことを1つ指摘したいと思います。文字列や記号をハッシュキーとして使用する傾向がありますが、それは他の種類のオブジェクトも使用できないという意味ではありません。実際、ほとんどすべてのものをハッシュキーとして使用できます。

# Numbers can be hash keys
{1 => "one"}[1] # "one"

# So can the Ruby kernel
{Kernel => 1}[Kernel] # 1

# You can store values for specific classes
{Kernel => 1, String => 2}["hello world".class] # 2

# You can store values for booleans
{true => "verdad"}[1==1] # "verdad"

# You can even use complex arrays and even other hashes as hash keys
{[[1,0],[0,1]] => "identity matrix"}[[[1,0], [0,1]]] # "identity matrix"

これらのオプションのいくつかは他のオプションよりも便利ですが、すべて利用できます。

デフォルト値を制御できます。

ハッシュh={ a: 1 }があるとします。 。存在しない値にアクセスしようとした場合-たとえば、h[:x] -あなたはゼロになります。これは、特に指定しない限り、nilがすべてのハッシュのデフォルト値であるためです。

コンストラクターに引数を渡すことで、新しいハッシュのデフォルト値を設定できます。

h = Hash.new("This attribute intentionally left blank")
h[:a] = 1
h[:a] # 1
h[:x] # "This attribute intentionally left blank"
動的なデフォルト値

これはトリックであり、その後に続くすべての基盤であるため、注意してください。

ブロックをコンストラクターに渡すと、プログラムでデフォルト値を生成できます。以下の例では、デフォルト値にタイムスタンプを追加したので、動的に生成されていることがわかります。

h = Hash.new { |hash, key| "#{key}: #{ Time.now.to_i }" }
h[:a] # "a: 1435682937"
h[:a] # "a: 1435682941"
h[:b] # "b: 1435682943"

「デフォルト値」ブロックはデフォルト値を返す以外のこともできるので、これは重要です。

ハッシュキーが存在しない場合に例外を発生させる

ハッシュの主な問題の1つは、ハッシュがサイレントに失敗することです。誤ってuser[:phnoe]と入力しました user[:phone]の代わりに 、そして例外を発生させる代わりに、ハッシュはnilを返します。ただし、この動作は変更できます。

h = Hash.new { |hash, key| raise ArgumentError.new("No hash key: #{ key }") }
h[:a]=1
h[:a] # 1
h[:x] # raises ArgumentError: No hash key: x

この手法は特定のハッシュに適用されるため、デバッグやリファクタリングに役立ちます。これは、Hashクラスにモンキーパッチを適用するようなものよりも、この動作を追加するための邪魔にならない方法です。

注:新しいコードでHash.fetchの代わりにこれを使用することを提案しているわけではありません。デバッグとリファクタリングのために準備を整えるのは、興味深いトリックです。

遅延生成されたルックアップテーブル

この手法は、計算結果をキャッシュするのに役立ちます。多くの平方根を計算する必要があると想像してください。以下の例のように、遅延が発生するルックアップテーブルを作成できます。

sqrt_lookup = Hash.new { |hash, key| hash[key] = Math.sqrt(key) }
sqrt_lookup[9] # 3.0
sqrt_lookup[7] # 2.6457513110645907
sqrt_lookup    # {9=>3.0, 7=>2.6457513110645907}
再帰的なレイジールックアップテーブル

再帰関数があり、各再帰の結果をキャッシュしたいとします。例として階乗計算を取り上げましょう。 「4階乗」、別名「4!」 「4x3x2x1」の別の言い方です。ハッシュを使用してこれを再帰的に実装できます。このブログ投稿から抜粋した以下の例は、それをうまく示しています:

factorial = Hash.new do |h,k| 
  if k > 1
    h[k] = h[k-1] * k
  else
    h[k] = 1
  end
end

factorial[4] # 24
factorial    # {1=>1, 2=>2, 3=>6, 4=>24}
初期化後のデフォルトの変更

ハッシュが作成された後、デフォルト値を制御することもできます。これを行うには、defaultを使用します およびdefault_proc セッター。

h={}
h[:a] # nil
h.default = "new default"
h[:a] # "new default"

h.default_proc = Proc.new { Time.now.to_i }
h[:a] # 1435684014

Rubyを探す:怠惰な無限のネストされたハッシュのゲーム

楽しみのために、これらの便利なテクニックをすべて1つの非常に役に立たない例にまとめましょう。その古いテキストベースのゲームアドベンチャーを覚えていますか?これまでで最も愚かなバージョンを作成しましょう。

あなたが洞窟にいると想像してみてください。あなたは北、南、東または西に行くことができます。これらの選択肢のうちの3つは、探索を続ける洞窟の新しい「部屋」に移動します。しかし、1つの選択はあなたを「ルビー」に導きます。したがって、ゲームの名前は「ルビーを見つける」です。

洞窟の各部屋はハッシュに対応しています。ハッシュにはエントリが1つだけあります。ランダムに選択された["n"、 "s"、 "e"、 "w"]のいずれかは、「ルビーを見つけました」という値を持ちます。間違って選択すると、新しいハッシュが作成されてツリーに追加されます。

generator = Proc.new do |hash, key| 
  hash[key] = Hash.new(&generator).merge(["n", "s", "e", "w"][rand(4)] => "You found the ruby!")
end
dungeon = Hash.new(&generator)
dungeon["n"] # <Hash ...
dungeon["n"]["s"] # <Hash ...
dungeon["n"]["s"]["w"] # "You found the ruby!"


  1. Ruby2.6の9つの新機能

    Rubyの新しいバージョンには、新しい機能とパフォーマンスの改善が含まれています。 変更についていきますか? 見てみましょう! 無限の範囲 Ruby 2.5以前のバージョンは、すでに1つの形式の無限範囲をサポートしています( Float ::INFINITY を使用) )、しかしRuby2.6はこれを次のレベルに引き上げます。 新しい無限の範囲 次のようになります: (1..) これは、(1..10)のような終了値がないため、通常の範囲とは異なります。 。 使用例 : [a, b, c].zip(1..) # [[a, 1], [b, 2], [c, 3]] [1,2,3,

  2. ハッシュテーブルの説明

    私のお気に入りのデータ構造の1つは、シンプルで強力なハッシュテーブルです。 キーと値のペアを格納する効率的な方法であるため、おそらく以前に使用したことがあります。 ハッシュテーブルの実装の背後には、研究する価値のある興味深いコンピュータサイエンスの概念がいくつかあります。これは、まさにこの記事で行うことです! バケットとハッシュ関数 ハッシュテーブルの基本的な考え方は、効率的に(O(1)で)できるようにすることです。 )キーでインデックス付けされたデータを取得します。 簡単に復習すると、Rubyでハッシュテーブルを使用すると次のようになります。 prices = { apple: