4

我的应用程序中有一个结构,我需要这样的哈希:

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

所以我希望键 3、4、5、6 和 7 具有相同的值。
当然上面的示例不起作用,因为 Ruby 是智能的并且将哈希键设置为给定:它将范围设置为键:) 所以我只能访问我作为my_hash[(3..7)]my_hash[3]my_hash[4]的值为零。
当然我可以在散列之外进行检查或构造来做我需要的事情,但是我很好奇是否可以在不使用散列声明之外的任何循环的情况下设置这样的散列?如果不是,什么是最优雅的?谢谢!

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

它的工作原理与普通哈希相同,除了使用 Range 键时,它会在 Range 中的每个点设置给定值。

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 回答
3

我不认为有一种方法可以使用文字哈希语法或不进行一些迭代来设置多个键,但这里有一个通过迭代来完成的简单方法:

irb(main):007:0> h = { 1 => [6,2,2], 2 => [7,4,5] }; (3..7).each {|n| h[n] = [7,2,1]}; h
=> {1=>[6, 2, 2], 2=>[7, 4, 5], 3=>[7, 2, 1], 4=>[7, 2, 1], 5=>[7, 2, 1], 6=>[7, 2, 1], 7=>[7, 2, 1]}

(请注意,尾随; h仅用于上面的显示目的。)

于 2013-04-01T21:10:20.160 回答
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 回答