Как добавить значение ко всем предыдущим значениям в массиве - PullRequest
2 голосов
/ 17 мая 2010

Допустим, у меня есть следующий массив:

my_array = [1, 5, 8, 11, -6]

Мне нужно перебрать этот массив и добавить значения до текущего значения вместе. Пример, вероятно, будет легче понять. Мне нужно вернуть массив, который должен выглядеть примерно так:

final_array = [1, 6, 14, 25, 19]

Я пытался сделать что-то вроде этого:

my_array.collect {|value| value + previous_values }

Но очевидно, что это не работает, потому что я не могу понять, как получить предыдущие значения в массиве.

Я нуб-программист, так что это может быть проще, чем я. Я почти уверен, что мне нужно использовать либо сбор, либо инъекцию, но я не могу понять, как это сделать.

Любая помощь будет оценена.

Ответы [ 6 ]

9 голосов
/ 17 мая 2010

Моим самым первым инстинктом было: «Это, очевидно, сканирование (он же префикс-сумма), так что это должно быть легко»:

[1, 5, 8, 11, -6].scan(:+)

Очевидно, что в последнее время я слишком много читаю на Haskell и Scala, потому что в Ruby & hellip нет Enumerable#scan; еще:

module Enumerable
  def scan(initial=first, &block)
    [initial].tap {|res| 
      reduce {|acc, el| 
        block.(acc, el).tap {|el|
          res << el
        }
      }
    }
  end
end

Если вы хотите, чтобы Enumerable#scan вел себя как Enumerable#reduce, т.е. принимал необязательный начальный аргумент и необязательный символ, нам нужно немного улучшить нашу версию с помощью некоторого аргумента, массирующего код, украденный из Enumerable#reduce Рубиниуса:

module Enumerable
  def scan(initial=nil, sym=nil, &block)
    args = if initial then [initial] else [] end
    unless block_given?
      args, sym, initial = [], initial, first unless sym
      block = ->(acc, el) { acc.send(sym, el) }
    end
    [initial || first].tap {|res| 
      reduce(*args) {|acc, el| 
        block.(acc, el).tap {|e|
          res << e
        }
      }
    }
  end
end

В этой расширенной версии теперь работает приведенный выше пример:

p [1, 5, 8, 11, -6].scan(:+)
# => [1, 6, 14, 25, 19]

Если у вас снова возникла такая проблема, на другом языке запомните термины scan и prefix-sum , такие функции обычно довольно распространены. Я не совсем понимаю, почему у Руби их уже нет.

6 голосов
/ 17 мая 2010

Ваша собственная попытка с collect была уже очень близка; просто продолжайте суммировать предыдущие значения на ходу.

my_array = [1, 5, 8, 11, -6]
previous_values = 0
my_array.collect { |value| previous_values += value }
# => [1, 6, 14, 25, 19]
3 голосов
/ 17 мая 2010
x = 0
[1, 5, 8, 11, -6].map {|a| x = x +a }
1 голос
/ 20 марта 2015

Я сделал для этого гем, который предварительно выделяет массив результатов. Операция очень быстрая, даже для Enumerables с большим количеством элементов. В отличие от решений, использующих карту Enumerable #, синтаксис точно такой же, как и у Enumerable # Reduce, и может дополнительно использовать Enumerable # Reduction под капотом в случае, если у вас есть исправленный обезьяной #reduce. Название взято из одноименной функции Clojure.

https://rubygems.org/gems/reductions

Для установки:

$ gem install reductions

Для использования:

require 'reductions'

[1, 5, 8, 11, -6].reductions(:+)            #=> [1, 6, 14, 25, 19]
[1, 5, 8, 11, -6].reductions{|a| a + b}     #=> [1, 6, 14, 25, 19]

# With an inital value:
[1, 5, 8, 11, -6].reductions(1,:+)          #=> [1, 2, 7, 15, 26, 20]
[1, 5, 8, 11, -6].reductions(1){|a| a + b}  #=> [1, 2, 7, 15, 26, 20]
1 голос
/ 17 мая 2010

Вы можете использовать это:

my_array = [1, 5, 8, 11, -6]
final_array = []

my_array.inject(0) {|res, it| final_array << res + it; res + it}
1 голос
/ 17 мая 2010
my_array.each_index{|i| my_array[i] += my_array[i-1] if i>0 }

или

my_array.inject([]){|memo, item| memo << item + memo.last.to_i }
...