Ruby哈希等价于Python dict的setdefault

12 投票
5 回答
2855 浏览
提问于 2025-04-18 01:12

在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 个回答

1

如果你只想修改 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]}
1

你可以简单地 在创建 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
3

在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构造函数来定义默认值,方法是传递一个额外的块或值。

5

我不想重复老话,但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'
9

哈希可以设置一个默认值,或者设置一个默认的过程(当某个键不存在时会调用这个过程)。

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([]) 这样写是行不通的,因为每个键都会使用同一个数组(也就是同一个对象)。

撰写回答