当Hash的默认值设置为Hash时,它将提供意外的输出

时间:2018-11-02 20:45:26

标签: ruby

我正在v2.3.7中尝试Ruby的默认Hash值。我对一个简单的测试用例中的输出感到惊讶,并且我想知道幕后发生的事情可以解释它。

foo = Hash.new({x: 0, y: 0}) # provide a default value 
foo['bar'][:x] += 1          # expect to add to the default value
foo                          # outputs `{}` ?! expected {'bar'=>{:x=>1,:y=>0}}
foo['bar']                   # outputs `{:x=>1, :y=>0}` as expected

为什么foo在第3行上似乎为空?我期望像{'bar'=>{:x=>1,:y=>0}}这样的输出。我是否缺少关于这种情况发生的超级基础知识? foo.empty?返回true,但是foo['bar']产生输出。

这是一个错误吗?

3 个答案:

答案 0 :(得分:3)

使用默认值时,未设置键/值。仅返回默认值而不是nil

我想您正在想象它的工作原理,其中对||=之类的访问键设置了默认值。

default = {x: 0, y: 0}
foo = Hash.new
foo['bar'] ||= default 
foo['bar'][:x] += 1

相反,它的工作原理是在没有密钥的情况下返回默认值。

default = {x: 0, y: 0}
foo = Hash.new
val = foo['bar'] || default
val[:x] += 1

换一种说法,您期望如此。

def [](key)
  @data[key] ||= default
end

但是它是这样的。

def [](key)
  @data[key] || default
end

  

但是,如果我提供一个整数而不是一个Hash作为默认值,则此行为似乎会改变。例如,如果我执行foo = Hash.new(1),则foo ['bar'] + = 1则是我所期望的行为。 foo不为空,并且默认值未更改。 – 4分钟前aardvarkk

foo['bar'] += 1确实是速记

default = foo['bar']        # fetch the default
foo['bar'] = default + 1    # sets 'bar' on foo

请注意,它将在[]=上调用foo

foo['bar'][:x] += 1是...的简写。

default = foo['bar']   # fetch the default value
val = default[:x]      # fetch :x from the default
default[:x] = val + 1  # set :x on the default value

请注意,它以默认值而不是[]=调用foo

答案 1 :(得分:2)

基本上,您正在更改默认值,而不是将新密钥分配给哈希。 可以通过调用哈希的任何键来理解,例如

foo['bar']
=> {:x=>1, :y=>0}
foo['foobar']
=> {:x=>1, :y=>0}

另一种查看方式

foo['bar'][:x] += 1
=> 2

foo['bar']
=> {:x=>2, :y=>0}

foo['bar'][:x] += 1
=> 3

foo['bar']
=> {:x=>3, :y=>0}

foo['bar'][:x] + 1
=> 4

foo['bar']
=> {:x=>3, :y=>0} # here the value is not assigned so not changed - as expected

答案 2 :(得分:0)

法汉·梅蒙(Farhan Memon)博士已经提到:

  

基本上,您正在更改默认值且未分配新密钥   散列。

但这将返回您想要的内容:

foo = Hash.new({x: 0, y: 0}) # provide a default value
foo[:whatever][:x] += 1
foo['bar'] = foo['bar']
foo #=> {"bar"=>{:x=>1, :y=>0}}

首先,您更改了默认值,然后将默认值分配给新键['bar']。也可以与foo['bar'] = foo[:whatever_else]一起使用。

foo['bar'] = foo[:whatever_else]
foo['baz'] = foo[:whatever_else]
foo #=> {"bar"=>{:x=>1, :y=>0}, "baz"=>{:x=>1, :y=>0}}