【问题标题】:What is meant: "Hash.new takes a default value for the hash, which is the value of the hash for a nonexistent key"含义:“Hash.new 采用哈希的默认值,即不存在键的哈希值”
【发布时间】:2014-05-23 14:10:17
【问题描述】:

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

不理解section 4.4.1中的这句话的意思:

相比之下,哈希是不同的。而数组构造函数 Array.new 为数组取一个初始值, Hash.new 取一个 散列的默认值,这是一个散列的值 不存在的密钥:

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

【问题讨论】:

  • 如果你不明白那段话的意思,那么也许你应该在英文网站上问这个。
  • @sawa 可能有点苛刻?
  • 感谢两位受访者,我现在明白了:)

标签: ruby dictionary hash


【解决方案1】:

您可以随时尝试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

【讨论】:

  • 你的第二个例子发生了什么?
  • 提供给 Hash.new 的参数定义了该哈希中任何其他未定义键的默认值。例如,将值初始化为 0 会很有用,这样每次看到某些内容时都可以执行 hash["key"] += 1。或者将它们全部初始化为数组,以便执行 hash["key"]
  • hash["key"] hash = Hash.new {Array.new} # setup a new array for each key => {} hash[:a] << 'something' => ["something"] a[:a] => []
【解决方案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

【讨论】:

    【解决方案3】:

    首先阅读 类方法 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=&gt;4 添加到哈希中!我重复一遍:它确实将键/值对添加到哈希中。这很重要!

    h[:chips] += 1
    

    是以下的简写:

    h[:chips] = h[:chips] + 1
    

    由于哈希 h 没有键 :chips 在计算等号右侧的 h[:chips] 时,它返回默认值 4,然后添加 1 使其成为 5 并且值被分配给h[:chips],它将键值对:chips=&gt;5 添加到哈希中,如下行所示。最后一行仅报告现有键 :chips 的值。

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

    h[k] += 1
    

    而不是

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

    或诀窍:

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

    (仅当哈希值不为 nil 时才有效)。顺便说一句,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] &lt;&lt; "jay" 导致h[:redbird] 发生变化?也许这可以解释这里发生了什么:

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

    h[:robin] 仅返回默认值,我们看到它已从“magpie”更改为“magpiejay”。现在查看object_id 的默认值以及与键:bluebird:redbird 关联的值。正如你所看到的,所有的值都是同一个对象,所以如果我们改变一个,我们就会改变所有其他的,包括默认值。现在很明显为什么h[:bluebird] &lt;&lt; "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 对您说:“哈希 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] =&gt; [] 添加到哈希中。你可以在街区内做任何你喜欢的事情;您既不需要也不限于为键指定值。实际上,在块中,Ruby 实际上是在说,“一个不在散列中的键已被引用而没有值。我给你这个引用和一个对散列的引用。这将允许你添加那个键如果这是您想要做的,则为哈希添加一个值,但是您在此块中所做的完全是您的业务。”

    哈希 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 最后一种形式的全部内容。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-06
      • 1970-01-01
      • 1970-01-01
      • 2010-10-20
      • 2021-11-01
      • 2013-04-16
      相关资源
      最近更新 更多