41

我以为我了解默认方法对哈希的作用...

如果键不存在,则为其提供默认值:

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

谁能解释发生了什么?

4

7 回答 7

54

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

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

它甚至可以嵌套:

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

另一种方法是:

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

但这有一个显着的副作用,即询问一个键会创建它,这会呈现 has_key? 相当没用,所以我避免使用这种方法。

于 2008-10-10T18:02:35.210 回答
51

Hash.default用于设置查询不存在的键时返回的默认值。集合中的条目不是为您创建的,只是因为对其进行了查询。

此外,您设置的值default是对象的实例(在您的情况下为 Array),因此当返回 this 时,可以对其进行操作。

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
于 2008-10-10T10:48:54.817 回答
34

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

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=>[]}
于 2008-10-11T16:39:26.407 回答
9

格伦麦当劳 说:

“另一种方法是:

myhash = Hash.new {|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?才不是。

于 2010-04-06T01:08:51.980 回答
9

如果你真的想要一个无限深的哈希:

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

当然,正如格伦在上面指出的那样,如果你这样做,has_key? 失去了它的意义,因为它总是会返回真。感谢 jbarnette 这个。

于 2010-11-23T01:03:05.130 回答
6
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
于 2008-10-10T11:00:11.643 回答
-4

我不确定这是否是您想要的,但是您可以这样做以在查询缺少的哈希键时始终返回一个空数组。

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]
   => []
于 2008-10-10T18:48:24.337 回答