2010-05-17 1 views
1

Допустим, у меня есть следующий массив:Как добавить значение для всех предыдущих значений в массиве

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

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

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

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

my_array.collect {|value| value + previous_values } 

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

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

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

+0

Круто. Всем спасибо. Я сделаю это. Очень ценится. – James

ответ

6

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

my_array = [1, 5, 8, 11, -6] 
previous_values = 0 
my_array.collect { |value| previous_values += value } 
# => [1, 6, 14, 25, 19] 
1
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 } 
+0

инъекция вряд ли будет работать как memo.last первоначально будет возвращать ноль. – Anton

+0

Да, вы правы. Я думал, что он автоматически преобразуется в int, но нет, поэтому явно добавляется 'to_i'. –

+0

Кажется излишне сложным сделать особый случай первого элемента в обоих решениях. – Arkku

1

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

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

my_array.inject(0) {|res, it| final_array << res + it; res + it} 
8

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

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

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

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, т.е. принимает необязательный начальный аргумент и необязательный символ, нам нужно немного улучшить нашу версию с некоторыми аргументом, втирание код украденных из Rubinius-х 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, такие функции обычно довольно распространены. Я не совсем понимаю, почему Ruby их уже нет.

+0

Я разделяю ваш инстинкт ... Это довольно смешно, что это не было встроенным с самого начала ... – Jonah

1

Я сделал для этого камень, который предварительно выделяет массив результатов. Операция выполняется очень быстро, даже для Enumerables с большим количеством элементов. В отличие от решений, использующих Enumerable # map, синтаксис точно подобен синтаксису Enumerable # reduce и может при необходимости использовать Enumerable # 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]