我在IRB上尝试了几行代码,然后我发现我无法修改从Hash返回的新数组作为默认值。以下是IRB会话,更详细地显示了我的情况:
a = Hash.new { Array.new }
#=> {}
a[2]
#=> []
a[2].push '2'
#=> ["2"]
a[2]
#=> []
a[2] = []
#=> []
a[2].push '2'
#=> ["2"]
a
#=> {2=>["2"]}
a[2].push '2'
#=> ["2", "2"]
a
#=> {2=>["2", "2"]}
为什么我无法修改不存在的密钥的默认值?
答案 0 :(得分:2)
Hash#new
的块形式如下所示:
b = Hash.new {|hash, key| hash[key] = Array.new}
#=> {}
b[2]
#=> []
b[2].push('2')
#=> ["2"]
b[2]
#=> ["2"]
b[3]
#=> []
b[3].push('3')
#=> ["3"]
b[3]
#=> ["3"]
答案 1 :(得分:2)
见Hash.new {|hash, key| block } → new_hash
。它说:
如果指定了一个块,则将使用哈希对象调用它 键,应该返回默认值。这是街区 如果需要,有责任将值存储在哈希值中。
这意味着您必须创建一个块,用于存储在创建新值或更新现有值时传递给哈希值的键值。因为,您的哈希定义不存储它:
a = Hash.new { Array.new }
无论你多少次尝试这样做,你都会失去价值:
a[2].push '2' #=> ["2"]
p a #=> {}
您正在做的是然后定义一个具有默认值的键:
a[2] = []
然后推送值:
a[2].push #=> ["2"]
p a #=> {2 => ["2"]}
对于这种情况,还有另一种方法可以将块定义为:
a = Hash.new { |h, k| h[k] = Array.new }
或:
a = Hash.new { |h, k| h[k] = [] }
如果您看到上面的块,它会说:键的默认值将是一个空数组,如果有任何对象被推入其中:
a[2].push '2' #=> ["2"]
它会将该对象存储到该数组中:
p a #=> {2=>["2"]}
答案 2 :(得分:2)
使用时没有问题:
a = Hash.new { Array.new }
如果我执行a[2]
,它将返回[]
,因为它是默认值。它只是告诉我它是什么,仅此而已。
因为默认值是作为块给出的,所以每次调用a[x]
时,默认值都是一个新的空数组。要理解的重要一点是,它只是一个空数组,不以任何方式与哈希a
绑定。也就是说,a[2]
只回答问题,"什么是默认值?&#34 ;;它没有做任何事情。 (如其他答案所示,如果块的编写方式不同,可以在返回默认值之前完成各种奇妙的事情。)
请注意,Hash.new { Array.new }
,Hash.new { [] }
和Hash.new { |h,k| Array.new }
都是等效的。
要将密钥2
添加到a
,a[2]
必须是左值。稍后您将在示例中使用它:
a[2] = []
但如果你这样做,就没有必要设置默认值。您要做的是将a[2]
设置为等于默认值:
a[2] = a[2]
(注意,如果在这种情况下,块的返回值不依赖于键,则上面的行等同于:
a[2] = a[123456]
如果没有密钥123456
。)
这会将键值对2=>[]
添加到哈希中,所以现在
a[2].push 2
a[2].push 3
a #=> {2=>[2, 3]}
然而,更有意义的是,只需一步即可:
a[2] = a[2].push 2
如果a
没有键2
,则相等右侧的a[2]
将等于空数组的默认值,2
将被推送到它和键2
的值将设置为[2]
。
如果a
已有密钥2
,请说a[2] = [3,4,5]
,右侧的a[2]
将为[3,4,5]
,2
将被推送在a
上(如果它没有2
以外的其他键)将是:
a #=> {2=>[3,4,5,2]]}