【发布时间】:2012-10-14 05:38:07
【问题描述】:
根据specification,用作哈希键的字符串被复制和冻结。其他可变对象似乎没有这种特殊考虑。例如,使用数组键,以下是可能的。
a = [0]
h = {a => :a}
h.keys.first[0] = 1
h # => {[1] => :a}
h[[1]] # => nil
h.rehash
h[[1]] # => :a
另一方面,类似的事情不能用字符串键来完成。
s = "a"
h = {s => :s}
h.keys.first.upcase! # => RuntimeError: can't modify frozen String
当涉及到哈希键时,为什么将字符串设计为与其他可变对象不同?是否有任何用例可以使该规范变得有用?该规范还有哪些其他后果?
我实际上有一个用例,其中缺少关于字符串的这种特殊规范可能很有用。也就是说,我使用
yaml gem 读取了一个手动编写的描述哈希的 YAML 文件。键可能是字符串,我想在原始 YAML 文件中允许不区分大小写。当我读取文件时,我可能会得到这样的哈希:
h = {"foo" => :foo, "Bar" => :bar, "BAZ" => :baz}
我想将键规范化为小写以获得这个:
h = {"foo" => :foo, "bar" => :bar, "baz" => :baz}
通过这样做:
h.keys.each(&:downcase!)
但由于上述原因返回错误。
【问题讨论】:
-
看起来,就我而言,我能做的最好的事情是
h.keys.each{|s| h.store(s.downcase, h.delete(s))}。 -
我只能猜测“为什么”。除了字符串是比数组更常见的用例外,我怀疑冻结字符串会更容易实现。如果我知道 Perl,我会看看 Ruby 是否试图在哈希行为上与 Perl 保持一致。如果我精通日语,我会查看何时实施冻结键,看看这是否是错误报告或邮件列表上讨论的结果(大概是日语,因为在 Ruby 的历史上如此早期的事情)。
-
@AndrewGrimm Here 它说数组和散列不能成为散列的好键,因为它们可以被修改,并且字符串被冻结,因此您不必调用 rehash。与 steenslag 的回答一致。
-
不确定这个方法是什么时候添加的,transform_keys!可以做
h.transform_keys!(&:downcase)