在此代码中如何减少或注入工作

how does reduce or inject work in this code

在 code wars 上找到了这个作为解决方案之一。有人可以向我解释 "args.reduce(self)" 在此代码中的工作原理吗?之后的块是有道理的。

config = { :files => { :mode => 0x777 }, :name => "config" }

class Hash
  def get_value( default, *args )
    args.empty? ? default : args.reduce(self) { |acum, key| acum.fetch(key) } rescue default
  end
end

config.get_value("", :files,:mode)

它假定 self 是一个哈希嵌套,并将 args 视为一个键序列,以便越来越深入地深入到该哈希嵌套中。

假设我们执行

{ :a=>{:b=>{:c=>3 } } }.get_value(4, :a, :b, :c)

所以在方法内

default #=> 4
args    #=> [:a, :b, :c]
self    #=> { :a=>{:b=>{:c=>3 } } }

我们接着执行下面的1:

args.empty? ? default : args.reduce(self) { |acum, key| acum.fetch(key) } rescue default
  #=> [:a, :b, :c].empty? ? 4 : [:a, :b, :c].reduce({ :a=>{:b=>{:c=>3 } } }) { |acum, key|
  #     acum.fetch(key) } rescue 4
  #=> 3

如果args #=> [:a, :b],我们执行如下:

[:a, :b].empty? ? 4 : [:a, :b].reduce({ :a=>{:b=>{:c=>3 } } }) { |acum, key|
  acum.fetch(key) } rescue 4
  #=> {:c=>3}

如果 args #=> [:a, :b, :cat],则引发 KeyError 异常并且内联 rescue returns default:

的值
[:a, :b, :cat].empty? ? 4 : [:a, :b, :cat].reduce({ :a=>{:b=>{:c=>3 } } }) { |acum, key|
  acum.fetch(key) } rescue 4
  #=> 4

如果args #=> [][].empty?true,那么再次返回default的值:

[].empty? ? 4 : [].reduce({ :a=>{:b=>{:c=>3 } } }) { |acum, key|
  acum.fetch(key) } rescue 4
  #=> 4

幸运的是,我们不再需要像 Ruby 2.3.0 中给我们的 Hash#dig 那样处理这些废话,让我们可以编写以下内容。

class Hash
  def get_value( default, *keys )
    keys.empty? ? default : dig(*keys) || default
  end
end

{ :a=>{:b=>{:c=>3 } } }.get_value(4, :a, :b, :c)
  #=> 3
{ :a=>{:b=>{:c=>3 } } }.get_value(4, :a, :b)
  #=> {:c=>3} 
{ :a=>{:b=>{:c=>3 } } }.get_value(4, :a, :b, :cat)
  #=> 4 
{ :a=>{:b=>{:c=>3 } } }.get_value(4)
  #=> 4  

请注意 dig 的默认接收者是 self

1 请注意,该代码的作者可以写 ...args.reduce(self) { |acum, key| acum.fetch(key, default) } 而不是 ...args.reduce(self) { |acum, key| acum.fetch(key) } rescue default。见 Hash#fetch.

selfconfig 散列本身。

reduce 接受一个参数 self (因此原始 config 散列)。

然后 accum 将在第一次迭代时分配该参数(原始 config 哈希)。

在每次迭代中,accum 将被重新分配 args 的每个键的(嵌套)值。

让我们看看下面的例子。

config = {
  :files => { :mode => 0x777 },
  :name => "config"
}

[:files, :mode].reduce(config) { |hash, key|
  # The value for the current key, which can be another hash.
  newhash = hash.fetch(key)

  # Log each iteration here to see what's happening.
  # p newhash

  # Return the value for next iteration.
  newhash
}

输出的是十六进制值0x777,由Ruby转换为十进制1911

在您使用 args.reduce(self) 的示例中,self 是作为第一个参数传递给块的初始值,即 config 哈希本身。 Array 混合在 Enumerable 中,这就是 reduce 的来源。更多信息在这里:http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-reduce


对于 reduce 的每次迭代,块变量包含以下值:

迭代 1

  • hash包含{ :files => { :mode => 0x777 }, :name => "config" };这是 config 哈希本身。
  • key包含:files;数组的第一项。
  • newhash 包含 {:mode=>1911},我们 return 并成为下一次迭代的第一个参数。

迭代 2

  • hash 包含 {:mode=>1911} 因为我们在上一次迭代中 returned newhash
  • key包含:mode;数组的第二项。
  • newhash包含1911; reduce 迭代已完成,这是最终值。