意思是:" Hash.new采用散列的默认值,这是不存在的键的散列值"

时间:2014-04-10 04:00:30

标签: ruby dictionary hash

我目前正在阅读Michael Hartl的Ruby on Rails教程

不理解section 4.4.1

>中此语句的含义
  相反,哈希是不同的。而数组构造函数   Array.new获取数组的初始值,Hash.new取一个   哈希的默认值,即哈希值的哈希值   不存在的密钥:

有人可以帮忙解释一下这是什么意思吗?我不明白作者在本书这一部分的上下文中有关哈希与数组的不同之处的尝试

3 个答案:

答案 0 :(得分:2)

您可以随时试用irbrails console中的代码,了解它们的含义。

Array.new
# => []

Array.new(7)
# => [nil, nil, nil, nil, nil, nil, nil]

h1 = Hash.new
h1['abc']
# => nil

h2 = Hash.new(7)
h2['abc']
# => 7

答案 1 :(得分:2)

数组和散列都有一个带值的构造函数方法。这个值的用途在两者之间是不同的。

对于数组,该值用于初始化数组(示例取自上述教程):

a = Array.new([1, 3, 2])
# `a` is equal to [1, 3, 2]

与数组不同,哈希的new构造函数不使用其传递的参数来初始化哈希。因此,例如,键入h = Hash.new('a', 1) 使用(key, value)a1初始化哈希值:

h = Hash.new('a', 1) # NO. Does not give you { 'a' => 1 }!

相反,将值传递给Hash.new会导致哈希在传递不存在的键时将该值用作默认值。通常,哈希为不存在的键返回nil,但是通过传递默认值,在这些情况下,哈希可以返回默认值:

nilHash = { 'x' => 5 }
nilHash['x']   # Return 5, because the key 'x' exists in nilHash
nilHash['foo'] # Returns nil, because there is no key 'foo' in nilHash

defaultHash = Hash.new(100)
defaultHash['x'] = 5
defaultHash['x'] # Return 5, because the key 'x' exists in defaultHash 

defaultHash['foo'] 
# Returns 100 instead of nil, because you passed 100
# as the default value for non-existent keys for this hash

答案 2 :(得分:2)

首先阅读类方法 Hash#new的文档。您将看到有三种形式:

new → new_hash
new(obj) → new_hash
new {|hash, key| block } → new_hash 

创建空哈希

第一个表单用于创建空哈希:

h = Hash.new #=> {}

更常见的是:

h = {}       #=> {} 

使用Hash#new创建哈希的另外两种方法是,当哈希值尚未包含密钥时,为键/值对建立默认值。

Hash.new带参数

您可以使用以下两种方式之一创建具有默认值的哈希:

Hash.new(<default value>)

h = Hash.new # or h = {}
h.default = <default value>

假设哈希的默认值是4;那就是:

h = Hash.new(4) #=> {}

h[:pop] = 7     #=> 7
h[:pop] += 1    #=> 8
h[:pop]         #=> 8
h               #=> {:pop=>8}
h[:chips]       #=> 4
h               #=> {:pop=>8}
h[:chips] += 1  #=> 5
h               #=> {:pop=>8, :chips=>5}
h[:chips]       #=> 5

请注意,默认值不会影响:pop的值。这是因为它是使用作业创建的:

h[:pop] = 7

h[:chips]本身只返回默认值(4); 将键/值对:chips=>4添加到哈希值!我再说一遍:将键/值对添加到哈希中。这很重要!

h[:chips] += 1

是:

的简写
h[:chips] = h[:chips] + 1

由于散列h在评估等号右侧的:chips时没有键h[:chips],因此返回默认值4,然后添加1使其成为5并将该值分配给h[:chips],这会将键值对:chips=>5添加到哈希值,如下一行所示。最后一行仅报告现有密钥:chips的值。

那你为什么要建立一个默认值呢?我冒昧的主要原因是能够用零初始化它,所以你可以使用:

h[k] += 1

而不是

k[k] = (h.key?(k)) ? h[k] + 1 : 1

或诀窍:

h[k] = (h[k] ||= 0) + 1

(仅当哈希值意图为非零时才有效)。顺便提一下,key?又名has_key?

我们可以将默认值设为字符串吗?当然:

h = Hash.new('magpie')

h[:bluebird]                #=> "magpie"
h                           #=> {}
h[:bluebird] = h[:bluebird] #=> "magpie"
h                           #=> {:bluebird=>"magpie"}
h[:redbird]  = h[:redbird]  #=> "magpie"
h                           #=> {:bluebird=>"magpie", :redbird=>"magpie"}
h[:bluebird] << "jay"       #=> "magpiejay"
h                           #=> {:bluebird=>"magpiejay", :redbird=>"magpiejay"}

你可能会在最后一行上摸不着头脑:为什​​么h[:bluebird] << "jay"导致h[:redbird]改变?也许这将解释这里发生了什么:

h[:robin]              #=> "magpiejay"
h[:robin].object_id    #=> 2156227520
h[:bluebird].object_id #=> 2156227520
h[:redbird].object_id  #=> 2156227520

h[:robin]只返回默认值,我们看到它已从&#34; magpie&#34;到&#34; magpiejay&#34;。现在查看object_id&s,了解默认值以及与键:bluebird:redbird相关联的值。如您所见,所有值都是同一个对象,因此如果我们更改一个,我们会更改所有其他值,包括默认值。现在很明显为什么h[:bluebird] << "jay"更改了默认值。

我们可以通过添加庄严的鹰来进一步澄清这一点:

h[:eagle]               #=> "magpiejay"
h[:eagle] += "starling" #=> "magpiejaystarling"
h[:eagle].object_id     #=> 2157098780
h #=> {:bluebird=>"magpiejay", :redbird=>"magpiejay", :eagle=>"magpiejaystarling"}

由于

h[:eagle] += "starling" #=> "magpiejaystarling"

相当于:

h[:eagle] = h[:eagle] + "starling"

我们在等号的右侧创建了一个新对象,并将其分配给h[:eagle]。这就是为什么键:bluebird:redbird的值不受影响且h[:eagle]具有不同object_id的原因。

如果我们写下Hash.new([])Hash.new({}),我们会遇到类似的问题。如果有理由使用这些默认值,我就不知道它们。它当然可以非常有用,默认值是一个空字符串,数组或散列,但为此你需要第三种形式的Hash.new,它需要一个块。

带有块的

Hash.new

我们现在考虑Hash#new的第三个也是最终版本,它采用一个块,如下所示:

Hash.new { |h,k| ??? }

你可能会期望这是一个非常复杂和微妙的,当然比其他两种形式的方法更难掌握。如果是这样,那你就错了。它实际上很简单,如果你认为它看起来像这样:

Hash.new { |h,k| h[k] = ??? }

换句话说,Ruby正在对你说,&#34;哈希h没有密钥k。你想要它的价值是什么?现在考虑以下内容:

h7 = Hash.new { |h,k| h[k]=7 }
hs = Hash.new { |h,k| h[k]='cat' }
ha = Hash.new { |h,k| h[k]=[] }
hh = Hash.new { |h,k| h[k]={} }

h7[:a] += 3            #=> 10
hs[:b] << 'nip'        #=> "catnip"
ha[:c] << 4 << 6       #=> [4, 6]
ha[:d] << 7            #=> [7]
ha                     #=> {:c=>[4, 6], :d=>[7]}
hh[:k].merge({b: 4})   #=> {:b=>4}
hh                     #=> {}
hh[:k].merge!({b: 4} ) #=> {:b=>4}
hh                     #=> {:k=>{:b=>4}}

请注意,您无法写入ha = Hash.new { |h,k| [] }(或等效地,ha = Hash.new { [] })并期望将h[k] => []添加到哈希中。您可以在街区内做任何您喜欢的事情;您既不需要也不限于为密钥指定值。实际上,Ruby实际上在块内,&#34;没有值的情况下引用了不在哈希中的键。我给你那个引用,也引用了哈希。这样你就可以在哈希中添加一个带有值的键,如果这是你想要做的,那么你在这个块中所做的就完全是你的事。&#34;

哈希值h7hshahh的默认值分别为7(尽管简单地输入7作为参数更容易),空字符串,空数组或空哈希。可能最后两个是Hash#new最常用的一个块,如:

array = [[:a, 1], [:b, 3], [:a, 4], [:b, 6]]
array.each_with_object(Hash.new {|h,k| h[k] = []}) { |(k,v),h| h[k] << v }
  #=> {:a=>[1, 4], :b=>[3, 6]}

真正关于Hash#new的最后一种形式。