как уменьшить или ввести работу в этом коде

Нашел это в кодовых войнах как одно из решений. Может кто-нибудь объяснить мне, как работает «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)

person Wolf_Tru    schedule 17.09.2016    source источник


Ответы (4)


Предположим, мы выполняем

{ :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 возвращает значение 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

К счастью, нам больше не приходится заниматься такой ерундой, как нам дали Hash#dig в Ruby 2.3.0, что позволяет нам написать следующее.

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) } rescue default автор этого кода мог написать ...args.reduce(self) { |acum, key| acum.fetch(key, default) }. См. раздел Hash#fetch.

person Cary Swoveland    schedule 17.09.2016

Он предполагает, что self — это гнездо хэшей, и обрабатывает args как последовательность ключей, чтобы погружаться все глубже и глубже в это гнездо хэшей.

person matt    schedule 17.09.2016

self — это сам хэш config.

reduce принимает аргумент, равный self (отсюда исходный хэш config).

Затем accum на первой итерации будет присвоен этот аргумент (который является исходным хэшем config).

На каждой итерации accum будет переназначаться с (вложенным) значением для каждого ключа args.

person Vishal    schedule 17.09.2016

Вместо этого давайте рассмотрим следующий пример.

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. Массив смешивается с 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}, которое мы возвращаем и которое становится первым аргументом следующей итерации.

Итерация 2

  • hash содержит {:mode=>1911}, потому что мы вернули newhash в предыдущей итерации.
  • key содержит :mode; второй элемент массива.
  • newhash содержит 1911; итерации сокращения выполнены, и это окончательное значение.
person Billy Onjea    schedule 17.09.2016