有人可以用简单的英语向我解释这个简单的ruby代码

时间:2015-06-23 20:50:35

标签: ruby hash

就哈希默认值而言,当您将块作为默认值传递时,此实例中会发生什么,例如:

hash = Hash.new {|hash, key| hash[key] = [] } 

我运行了以下代码:

hash[:one] << "uno"
hash[:two] << "dos"  
hash[:three] 
hash[:four]

hash #returns the hash 

,输出为:

{:two=>["dos"], :three=>[], :four=>[], :one=>["uno"]} 

有人可以用简单的英语解释一下会发生什么吗?

1 个答案:

答案 0 :(得分:2)

目标

让我们从这里开始:

h = {}
h[:a] = [] unless h.key?(:a)
h[:a] << 7

我们创建一个空哈希h。此哈希的每个值都是一个值数组。我们希望将7附加到数组:a的值。如果h已经有一个密钥:a,我们会有类似h[:a] #=> [3,5]的内容,因此我们只需将7附加到该数组:

h[:a] << 7 #=> [3,5,7]

但是,如果h没有密钥:a(所以h[:a] #=> nil),我们首先需要将h[:a]设置为空数组。因此需要:

h[:a] = [] unless h.key?(:a)

如果h无法拥有值为:a的密钥nil(换句话说,如果没有值是nil),我们可以改为:

h[:a] = [] unless h[:a]

类似Ruby的技巧

我们可以通过几种方式使其更像Ruby。第一个是:

h = {}
(h[:a] ||= []) << 7 #=> [7] 
h #=> {:a=>[7]} 
(h[:a] ||= []) << 9 #=> [7, 9] 
h #=> {:a=>[7, 9]}

当Ruby看到:

h[:a] ||= []

她做的第一件事就是把它转换成:

h[:a] = h[:a] || []

步骤如下:

(h[:a] = h[:a] || []) << 7
  #=> (h[:a] = nil || []) << 7
  #=> (h[:a] = []) << 7
  #=> h[:a] << 7
h[:a] #=> [7]    
(h[:a] = h[:a] || []) << 9
  #=> (h[:a] = [7] || []) << 9
  #=> (h[:a] = [7]) << 9
  #=> h[:a] << 9
h[:a] #=> [7,9]

类似Ruby的默认值

第二种类似Ruby的方法与第一种方法相同,但是通过为哈希定义默认值来实现。如果哈希h没有键k,则h[k]将返回默认值。这也改变了哈希吗?我们暂时解决这个问题。

Hash::new的文档解释了有两种方法可以定义默认值。

错误的默认值

第一个,为了使默认值为空数组,是:

h = Hash.new([]) #=> {}

所以,如果我们现在写:

h[:a] #=> []

返回默认值([])。它不会改变哈希:

h #=> {}

我们可能想写:

h[:a] << 7
  #=> [] << 7 => [7]

h[:a]返回默认值,因为没有键:a。)如您所见,这不会改变哈希值,它只会返回一个未附加的数组[7]什么都是垃圾收集。

现在让我们试试这个:

(h[:a] = h[:a]) << 7
  #=> (h[:a] = []) << 7 => [7]
h #=> {:a=>[7]} 
(h[:a] = h[:a]) << 9
  #=> (h[:a] = [7]) << 9 => [7, 9] 
h #=> {:a=>[7, 9]} 

到目前为止,这么好,但是有一个问题:

(h[:b] = h[:b]) << 11 #=> [7, 9, 11] 
h #=> {:a=>[7, 9, 11], :b=>[7, 9, 11]} 
(h[:b] = h[:b]) << 13 #=> [7, 9, 11, 13] 
h #=> {:a=>[7, 9, 11, 13], :b=>[7, 9, 11, 13]} 

如您所见,只有一个默认空数组。

正确的默认值

幸运的是,还有另一种指定默认数组的方法:给Hash#new一个块。 (我们终于达到了问题的重点。)

如果散列h没有密钥kh[k]将调用该块。该块有两个块变量hk。您可以在块中执行您想要的任何内容。例如:

@a = 3
h = Hash.new { |h,k| @a = 7 }
h[:a] #=> 7
h     #=> {}
@a    #=> 7

现在我非常怀疑你是否想要这样做,但你可以。对于当前问题,您希望新键的值为空数组:

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

(小心。如果我们执行:

puts h[:a]
  #=> []
然后我们

h #=> { :a=>[] }

所以:

h[:a] << 7

首先调用该块,因为h没有键k,所以我们现在有:

{ :a=>[] }[:a] << 7
h #=> { :a=>[7] }

现在我们执行:

h[:a] << 9
h #=> {:a=>[7, 9]}   

未调用该块,因为h有一个键:a

关于它。有问题吗?