Аккумулятор для each_with_object продолжает повторную инициализацию - PullRequest
0 голосов
/ 21 февраля 2019

Я пытаюсь написать обобщенное декартово произведение, где входные данные [n1, n2, ... ni] производят выходные данные, которые являются массивом [m1, m2, ... mi] для всех mj, такихчто 0 <= mj <nj.Я понимаю, что приведенная ниже процедура создаст несколько сложную версию этого, но я стараюсь сохранить пример кода как можно более простым.Моя непосредственная проблема заключается в том, что вторая переменная блока (аккумулятор), которую, как я понимаю, должен обновляться для каждой итерации блока, не делает этого: </p>

#!/usr/bin/ruby

def gcp(dims)
  first = dims.shift
  dims.each_with_object((0...first).to_a) do |dim, v|
    puts "\nv: #{v}, dim: #{dim}"
    p v.product((0...dim).to_a)
  end
end

gcp([3,2,4])

Это приводит к следующему выводу:

v: [0, 1, 2], dim: 2
[[0, 0], [0, 1], [1, 0], [1, 1], [2, 0], [2, 1]]

v: [0, 1, 2], dim: 4
[[0, 0], [0, 1], [0, 2], [0, 3], [1, 0], [1, 1], [1, 2], [1, 3], [2, 0], [2, 1], [2, 2], [2, 3]]

Метод p является промежуточным, поэтому возвращаемое значение блока должно быть [[0, 0], [0, 1], [1, 0], [1, 1], [2, 0], [2, 1]] на первой итерации, и это должно быть значение v на второй итерации, если только я серьезнонеправильно понять each_with_object.

Ответы [ 2 ]

0 голосов
/ 21 февраля 2019

Каждая итерация получает один и тот же объект, поэтому вам необходимо либо изменить объект внутри блока, либо использовать reduce.

def gcp(dims)
  first = dims.shift
  dims.reduce((0...first).to_a) do |v, dim|
    puts "\nv: #{v}, dim: #{dim}"
    p v.product((0...dim).to_a)
  end
end

gcp([3,2,4])

Результат:

v: [0, 1, 2], dim: 2
[[0, 0], [0, 1], [1, 0], [1, 1], [2, 0], [2, 1]]

v: [[0, 0], [0, 1], [1, 0], [1, 1], [2, 0], [2, 1]], dim: 4
[[[0, 0], 0], [[0, 0], 1], [[0, 0], 2], [[0, 0], 3], [[0, 1], 0], [[0, 1], 1], [[0, 1], 2], [[0, 1], 3], [[1, 0], 0], [[1, 0], 1], [[1, 0], 2], [[1, 0], 3], [[1, 1], 0], [[1, 1], 1], [[1, 1], 2], [[1, 1], 3], [[2, 0], 0], [[2, 0], 1], [[2, 0], 2], [[2, 0], 3], [[2, 1], 0], [[2, 1], 1], [[2, 1], 2], [[2, 1], 3]]
0 голосов
/ 21 февраля 2019

Признаюсь, я не до конца понимаю вопрос, но я обратился к аналогичной проблеме, которая может объяснить, почему v не обновляется вашим кодом.

Давайте пройдемся по вашему коду, возвращаяжелаемый результат, а не отображать его по пути.

dims = [3,2,4]

first = dims.shift
  #=> 3 
dims
  #=> [2, 4]     dims

Выражение

dims.each_with_object((0...first).to_a) do |dim, v|
  v.product((0...dim).to_a)
end

фактически совпадает с

v = []
dims.each do |dim|
  v.product((0...dim).to_a)
end
v #=> []

То, что v являетсявсе же пустой массив в конце не должен быть сюрпризом, так как значение v не изменяется внутри цикла.Возвращаемое значение v.product((0...dim).to_a) выбрасывается в космос и больше никогда не будет видно.Вам нужен оператор присваивания внутри цикла.

Теперь рассмотрим следующее.

dims = [3,2,4]

v = []
dims.each do |n|
  v << (0...n).to_a
end
v #=> [[0, 1, 2], [0, 1], [0, 1, 2, 3]] 

(или v.push((0..n).to_a)).Чтобы использовать Enumerable # each_with_object , мы изменили бы приведенный выше код, удалив первый (v = []) и последний (v) операторы, заменив each на each_with_object([]) (аргумент, являющийся начальным значением)объекта, который будет возвращен методом) и добавьте блочную переменную v, которая содержит объект:

dims.each_with_object([]) do |n,v|
  v << (0...n).to_a
end
  #=> [[0, 1, 2], [0, 1], [0, 1, 2, 3]] 

Мы можем упростить это, используя Emumerable # map :

dims.map do |n|
  (0...n).to_a
end
  #=> [[0, 1, 2], [0, 1], [0, 1, 2, 3]] 

В зависимости от ваших потребностей, вы можете использовать Emumerable # flat_map :

dims.flat_map do |n|
  (0...n).to_a
end
  #=> [0, 1, 2, 0, 1, 0, 1, 2, 3]
...