Ruby哈希等价于Python dict的setdefault
在Python中,你可以在读取一个字典(也叫哈希表)的键的同时,如果这个键不存在,就把它设置为一个默认值。
举个例子:
>>> d={'key': 'value'}
>>> d.setdefault('key', 'default')
'value' # returns the existing value
>>> d.setdefault('key-doesnt-exist', 'default')
'default' # sets and returns default value
>>> d
{'key-doesnt-exist': 'default', 'key': 'value'}
那么在Ruby的哈希表中有没有类似的做法呢?如果没有,Ruby中通常是怎么处理这种情况的呢?
5 个回答
如果你只想修改 setdefault
返回的值,可以通过 Hash#merge!
来实现:
Python:
>>> d = {}
>>> d.setdefault("k", []).append("v")
>>> d
{'k': ['v']}
Ruby:
[28] pry(main)> h = {}
=> {}
[29] pry(main)> h.merge!(k: [:v]) { |_key, old, new| old.concat(new) }
=> {:k=>[:v]}
[30] pry(main)> h
=> {:k=>[:v]}
你可以简单地 在创建 Hash
时传入一个块:
hash = Hash.new do |hash, key|
hash[key] = :default
end
当你尝试访问一个不存在的键时,这个块会被调用。它会接收到哈希对象和你要访问的键。你可以对它们做任何事情,比如给这个键设置一个默认值,或者根据这个键生成一个新值等等。
如果你已经有了一个 Hash
对象,你可以使用 这个 default_proc=
方法:
hash = { key: 'value' }
# ...
hash.default_proc = proc do |hash, key|
hash[key] = :default
end
在Python中没有和这个功能完全相同的东西。不过,你可以通过一种叫做“猴子补丁”的方法来实现这个功能:
class Hash
def setdefault(key, value)
if self[key].nil?
self[key] = value
else
self[key]
end
end
end
h = Hash.new
h = { 'key' => 'value' }
h.setdefault('key', 'default')
# => 'value'
h.setdefault('key-doesnt-exist', 'default')
# => 'default'
不过要记住,在某些代码环境中,猴子补丁通常被视为一种禁忌。
猴子补丁的黄金法则是:你可以这么做,但并不意味着你应该这么做。
更常见的做法是通过Hash构造函数来定义默认值,方法是传递一个额外的块或值。
我不想重复老话,但setDefault的作用更像是hash中的fetch。它和hash中的default的作用是不一样的。一旦你在hash上设置了default,任何缺失的键都会使用这个默认值。但setDefault就不是这样了。它只会为一个缺失的键存储值,前提是找不到这个键。这个“存储新的键值对”的部分就是它和fetch的不同之处。
同时,我们现在的做法就像这样实现setDefault:
h = {}
h['key'] ||= 'value'
Ruby继续强调这个观点:
h.default = "Hey now"
h.fetch('key', 'default') # => 'value'
h.fetch('key-doesnt-exist', 'default') # => 'default'
# h => {'key' => 'value'}
h['not your key'] # => 'Hey now'
Python:
h = {'key':'value'}
h.setdefault('key','default') # => 'value'
h.setdefault('key-doesnt-exist','default') # => 'default'
# h {'key': 'value', 'key-doesnt-exist': 'default'}
h['not your key'] # => KeyError: 'not your key'
哈希可以设置一个默认值,或者设置一个默认的过程(当某个键不存在时会调用这个过程)。
h = Hash.new("hi")
puts h[123] #=> hi
# change the default:
h.default = "ho"
在上面的例子中,哈希保持为空。
h = Hash.new{|h,k| h[k] = []}
h[123] << "a"
p h # =>{123=>["a"]}
Hash.new([])
这样写是行不通的,因为每个键都会使用同一个数组(也就是同一个对象)。