使用具有默认值的哈希

Working with Hashes that have a default value

正在学习使用 ruby 编码。我正在学习哈希,但我不理解这段代码:count = Hash.new(0)。它说 0 是默认值,但是当我在 irb 上 运行 它时,它给了我一个空的散列 {}。如果 0 是默认值,为什么我看不到 count ={0=>0} 之类的内容。或者零是一个累加器但不转到键或值?谢谢

如果您尝试访问不存在的哈希中的键,0 将是后备方案

例如:

count = Hash.new -> count['key'] => nil

count = Hash.new(0) -> count['key'] => 0

Hash.new 文档对此不是很清楚。我希望下面的示例可以阐明 Hash.new(0).

的区别和常用用法之一

第一段代码使用Hash.new(0)。 hash的默认值为0,当遇到新的key时,它们的值为0。这个方法可以用来统计数组中的字符。

第二段代码失败,因为键的默认值(未分配时)是 nil。该值不能加法使用(计数时),会产生错误。

count = Hash.new(0)

puts "count=#{count}"
# count={}

%w[a b b c c c].each do |char|
  count[char] += 1
end

puts "count=#{count}"
# count={"a"=>1, "b"=>2, "c"=>3}


count = Hash.new

puts "count=#{count}"

%w[a b b c c c].each do |char|
  count[char] += 1
  # Fails: in `block in <main>': undefined method `+' for nil:NilClass (NoMethodError)
end

puts "count=#{count}"

另请参见:

扩展@jeremy-ramos 的回答和@mu-is-too-short 的评论。

在这种方式下,默认哈希值有两个常见问题。

1。意外共享引用。

Ruby 使用您传入的内存中的 完全相同的 对象作为每个丢失的键的默认值。

对于不可变对象(如0),没有问题。但是你可能想写这样的代码:

hash = Hash.new([])
hash[key] << value

hash = Hash.new({})
hash[key][second_key] = value

这不会达到您的预期。而不是 hash[unknown_key] return 创建一个新的空数组或散列,它将 return 每个键的完全相同的 array/hash 对象。

这样做:

hash = Hash.new([])
hash[key1] << value1
hash[key2] << value2

生成一个散列,其中 key1key2 都指向包含 [value1, value2]

的同一个数组对象

解决方案

要解决这个问题,您可以创建一个带有默认块参数的散列(每当访问丢失的键时调用它,并允许您为丢失的键分配一个值)

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

2。使用默认值分配丢失的键

当您访问默认值为 return 的缺失键时,您可能希望散列现在包含该键且值为 returned。它不是。 Ruby 不修改散列,它只是 return 的默认值。所以,例如:

hash = Hash.new(0) #$> {} 
hash.keys.empty? #$> true
hash[:foo] #$> 0
hash[:foo] == 0 #$> true
hash #$> {}
hash.keys.empty? #$> true

解决方案

这种混淆也可以使用块方法解决,其中可以显式设置键值。

TL;DR 当您使用 Hash.new 初始化散列时,您可以设置默认值或默认过程(如果给定键不存在将返回的值)

关于理解这个魔法的问题首先你需要知道 Ruby 哈希有默认值。要访问默认值,您可以使用 Hash#default 方法

这个默认值默认:)是nil

hash = {}

hash.default # => nil

hash[:key] # => nil

您可以使用 Hash#default=

设置默认值
hash = {}

hash.default = :some_value

hash[:key] # => :some_value

非常重要的注意事项:默认使用可变对象是危险的,因为会产生如下副作用:

hash = {}
hash.default = []

hash[:key] # => []

hash[:other_key] << :some_item # will mutate default value

hash[:key] # => [:some_value]
hash.default # => [:some_value]

hash # => {}

要避免这种情况,您可以使用 Hash#default_proc and Hash#default_proc= 方法

hash = {}

hash.default_proc # => nil

hash.default_proc = proc { [] }

hash[:key] # => []

hash[:other_key] << :some_item # will not mutate default value
hash[:other_key] # => [] # because there is no this key

hash[:other_key] = [:symbol]
hash[:other_key] << :some_item
hash[:other_key] # => [:symbol, :some_item]

hash[:key] # => [] # still empty array as default

设置default取消default_proc,反之亦然

hash = {}

hash.default = :default

hash.default_proc = proc { :default_proc }

hash[:key] # => :default_proc

hash.default = :default

hash[:key] # => :default

hash.default_proc # => nil

回到Hash.new

当您将参数传递给此方法时,您会初始化默认值

hash = Hash.new(0)

hash.default # => 0
hash.default_proc # => nil

当您将块传递给此方法时,您会初始化默认过程

hash = Hash.new { 0 }

hash.default # => nil
hash[:key] # => 0