4

私のアプリケーションには、次のようなハッシュが必要な構造があります。

{ 1 => [6,2,2], 2 => [7,4,5], (3..7) => [7,2,1] }

したがって、キー3、4、5、6、および7に同じ値を設定したいと思います
。Rubyはインテリジェントであり、与えられたようにハッシュキーを設定するため、上記の例は機能しません:範囲をキーとして設定します:)したがって、アクセスすることしかできませんmy value as my_hash[(3..7)]and などmy_hash[3]my_hash[4]nil です。
確かに、必要なことを行うためにハッシュの外でチェックまたは構築を行うことができますが、ハッシュ宣言の外でループを使用せずにこのようなハッシュを設定できるかどうか興味がありますか? そうでない場合、最もエレガントなものは何ですか? ありがとう!

4

6 に答える 6

5

Hashこのようなハッシュを簡単に作成できるようにサブクラス化できます。

class RangedHash < Hash
  def []=(key, val)
    if key.is_a? Range
      key.each do |k|
        super k, val
      end
    else
      super key, val
    end
  end
end

範囲キーを使用する場合を除いて、通常のハッシュと同じように機能し、範囲内のすべてのポイントで指定された値を設定します。

irb(main):014:0> h = RangedHash.new
=> {}
irb(main):015:0> h[(1..5)] = 42
=> 42
irb(main):016:0> h[1]
=> 42
irb(main):017:0> h[5]
=> 42
irb(main):018:0> h['hello'] = 24
=> 24
irb(main):019:0> h['hello']
=> 24
于 2013-04-01T21:16:48.707 に答える
4

これって特に問題ないですか?

myhash = { 1 => [6,2,2], 2 => [7,4,5] }
(3..7).each { |k| myhash[k] = [7,2,1] }
于 2013-04-01T21:17:08.253 に答える
2

範囲内の可能なエントリごとに個別のキーと値のペアを作成するという考えは好きではありません。特に広い範囲では、まったくスケーラブルではありません。この小さな範囲を考えてみましょう:

'a' .. 'zz'

これにより、702 個の追加のキーが発生します。('a'..'zz').to_a楽しんでみてください。どうぞ。待ちます。

キーを作成する代わりに、ルックアップをインターセプトします。RangedHashクラス名の再利用:

class RangedHash < Hash
  def [](key)
    return self.fetch(key) if self.key? key

    self.keys.select{ |k| k.is_a? Range }.each do |r_k|
      return self.fetch(r_k) if r_k === key
    end

    nil
  end
end

foo = RangedHash.new
foo[1]    = [6,2,2]
foo[2]    = [7,4,5]
foo[3..7] = [7,2,1]

この時点でfoo次のようになります。

{1=>[6, 2, 2], 2=>[7, 4, 5], 3..7=>[7, 2, 1]}

メソッドのテスト:

require 'pp'
3.upto(7) do |i|
  pp foo[i]
end

どの出力:

[7, 2, 1]
[7, 2, 1]
[7, 2, 1]
[7, 2, 1]
[7, 2, 1]

範囲内の任意の値について、その範囲に関連付けられた値を出力します。範囲外の値でもハッシュで定義されているnil場合は、ハッシュに存在しないキーを返す場合と同様に、正常に機能します。そして、ハッシュを可能な限り小さく保ちます。

これの欠点、または質問に対する解決策は、範囲であるキーが重複して衝突を引き起こす可能性があることです。提案されたソリューションのほとんどでは、キーが互いに踏みつけられ、結果として悪い値が返される可能性があります。範囲キーを上書きするには直接競合が発生するため、このメソッドはそれを行いません。

これを修正するには、オーバーラップを許可するかどうかを決定する必要があります。許可する場合、最初に見つかったものが返されても問題ないか、または「最適」、つまり適合する最小の範囲を決定するロジックが必要か、またはいくつかの他の基準は完全に。または、値が同じ場合、オーバーラップを結合してより大きな範囲を作成する必要がありますか? ワームの缶詰です。

于 2013-04-02T00:37:23.040 に答える
1

ハッシュに直接パッチを当てますが、それ以外はルークと同じアイデアです...

class Hash
  alias_method :orig_assign, '[]='
  def []= k, v
    if k.is_a? Range
      k.each { |i| orig_assign i, v }
      v
    else
      orig_assign k, v
    end
  end
end

t = {}
t[:what] = :ever
t[3..7] = 123
p t # => {5=>123, 6=>123, 7=>123, 3=>123, 4=>123, :what=>:ever}
于 2013-04-01T21:19:21.540 に答える
0

ここにいくつかのより多くのアプローチがあります:

h = { 1 => [6,2,2], 2 => [7,4,5], (3..7) => [7,2,1] } 

def my_hash(h,y)
  h.keys.each do |x|
    if (x.instance_of? Range) and (x.include? y) then
      return p h[x]
    end
  end
p h[y]
end

my_hash(h,2)
my_hash(h,3)
my_hash(h,1)
my_hash(h,10)
my_hash(h,5)
my_hash(h,(3..7))

出力:

[7, 4, 5]
[7, 2, 1]
[6, 2, 2]
nil
[7, 2, 1]
[7, 2, 1]
于 2013-04-01T21:19:26.823 に答える