红宝石哈希.DEFAULT设置为一个列表

问题描述:

我想我明白了默认的方法做一个哈希...红宝石哈希.DEFAULT设置为一个列表

给一个键的默认值,如果不存在的话:

irb(main):001:0> a = {} 
=> {} 
irb(main):002:0> a.default = 4 
=> 4 
irb(main):003:0> a[8] 
=> 4 
irb(main):004:0> a[9] += 1 
=> 5 
irb(main):005:0> a 
=> {9=>5} 

一切都好。

但如果我设置的默认是一个空列表,或空哈希,我不知道这是在所有 ....

irb(main):001:0> a = {} 
=> {} 
irb(main):002:0> a.default = [] 
=> [] 
irb(main):003:0> a[8] << 9 
=> [9]       # great! 
irb(main):004:0> a 
=> {}       # ?! would have expected {8=>[9]} 
irb(main):005:0> a[8] 
=> [9]       # awesome! 
irb(main):006:0> a[9] 
=> [9]       # unawesome! shouldn't this be [] ?? 

我希望/预期的行为相同的行为就好像我已经使用|| =操作符...

irb(main):001:0> a = {} 
=> {} 
irb(main):002:0> a[8] ||= [] 
=> [] 
irb(main):003:0> a[8] << 9 
=> [9] 
irb(main):004:0> a 
=> {8=>[9]} 
irb(main):005:0> a[9] 
=> nil 

任何人都可以解释发生了什么事吗?

Hash.default用于设置默认值返回当您查询不存在的键时。收集中的条目不是为您创建的,只是因为查询它。

此外,您将default设置为的对象是一个对象(在您的情况下为数组)的实例,因此返回时可以对其进行操作。

a = {} 
a.default = []  # set default to a new empty Array 
a[8] << 9   # a[8] doesn't exist, so the Array instance is returned, and 9 appended to it 
a.default   # => [9] 
a[9]    # a[9] doesn't exist, so default is returned 
+1

很好的解释,有道理 – 2008-10-10 11:01:09

+1

我想指出,这种行为是不同于python的defaultdict,其中类似的代码工作得很好。 – 2012-10-10 07:57:03

irb(main):002:0> a.default = [] 
=> [] 
irb(main):003:0> a[8] << 9 
=> [9]       # great! 

有了这个声明,您已修改了默认;你还没有创建一个新的数组并添加了“9”。在这一点上,它是相同的,如果你做了这个:

irb(main):002:0> a.default = [9] 
=> [9] 

因此,它是毫不奇怪,你现在得到这个:

irb(main):006:0> a[9] 
=> [9]       # unawesome! shouldn't this be [] ?? 

此外,“< <”添加“9”阵列;它并没有将它添加到散列,这也解释了这一点:不是

irb(main):004:0> a 
=> {}       # ?! would have expected {8=>[9]} 

使用.DEFAULT,你可能想在你的程序做的是这样的:

# Time to add a new entry to the hash table; this might be 
# the first entry for this key.. 
myhash[key] ||= [] 
myhash[key] << value 

这是一个非常有用的成语:

(myhash[key] ||= []) << value 

它甚至可以被嵌套:

((myhash[key1] ||= {})[key2] ||= []) << value 

另一种方式是做:

myhash = Hash.new {|hash,key| hash[key] = []} 

但这有显著副作用,即询问约一键将创建它,这使得对象的has_key?相当无用,所以我避免了这种方法。

我不确定这是否是你想要的,但是你可以这样做,当查询丢失的散列键时总是返回一个空数组。

h = Hash.new { [] } 
h[:missing] 
    => [] 

#But, you should never modify the empty array because it isn't stored anywhere 
#A new, empty array is returned every time 
h[:missing] << 'entry' 
h[:missing] 
    => [] 

我认为这是您正在寻找的行为。这将自动初始化哈希任何新键的数组:

irb(main):001:0> h = Hash.new{|h, k| h[k] = []} 
=> {} 
irb(main):002:0> h[1] << "ABC" 
=> ["ABC"] 
irb(main):003:0> h[3] 
=> [] 
irb(main):004:0> h 
=> {1=>["ABC"], 3=>[]} 

格伦·麦克唐纳说:

“的另一种方法是做:

myhash = Hash.new {|哈希, key | hash [key] = []}

但是这具有明显的副作用,即询问关键字是否会创建它,这使得has_key无用,所以我避免了这种方法。

这实际上并不是真的。

irb(main):004:0> a = Hash.new {|hash,key| hash[key] = []} 
=> {} 
irb(main):005:0> a.has_key?(:key) 
=> false 
irb(main):006:0> a[:key] 
=> [] 
irb(main):007:0> a.has_key?(:key) 
=> true 

访问重点将创建它,因为我所期望的。仅仅问has_key?才不是。

如果你真的想有一个深不休哈希:

endless = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) } 
endless["deep"]["in"]["here"] = "hello" 

当然,正如上面格伦指出,如果你做到这一点,对象的has_key?它的意义会失去它的意义,因为它总会回归真实。 Thx to jbarnette为这一个。