Подтвердить что ты не робот

Сумма суммарного массива в Ruby

Каков самый гладкий, самый Ruby-подобный способ вычисления суммарной суммы массива?

Пример:

[1,2,3,4].cumulative_sum

должен возвращать

[1,3,6,10]
4b9b3361

Ответ 1

class Array
  def cumulative_sum
    sum = 0
    self.map{|x| sum += x}
  end
end

Ответ 2

Вот один из способов

a = [1, 2, 3, 4]
a.inject([]) { |x, y| x + [(x.last || 0) + y] }

Если все в порядке, что ответ более чем один оператор, то это будет чище:

outp = a.inject([0]) { |x, y| x + [x.last + y] }
outp.shift # To remove the first 0

Ответ 3

 irb> a = (1..10).to_a
 #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 irb> a.inject([0]) { |(p,*ps),v| [v+p,p,*ps] }.reverse[1..-1]
 #=> [1, 3, 6, 10, 15, 21, 28, 36, 45, 55]

Мы также можем взять заметку из Haskell и сделать рубиновую версию scanr.

irb> class Array
   >   def scanr(init)
   >     self.inject([init]) { |ps,v| ps.unshift(yield(ps.first,v)) }.reverse
   >   end
   > end
#=> nil
irb> a.scanr(0) { |p,v| p + v }
=> [0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55]
irb> a.scanr(0) { |p,v| p + v }[1..-1]
=> [1, 3, 6, 10, 15, 21, 28, 36, 45, 55]
irb> a.scanr(1) { |p,v| p * v }
=> [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]

Ответ 4

Также вы можете прочитать о scanl - функции, которая вам нужна, но это пока не реализовано в Ruby, насколько я знаю. Вот примеры и пример исходного кода: http://billsix.blogspot.com/2008/11/functional-collection-patterns-in-ruby.html

UPD: Вышеупомянутая ссылка мертва, поэтому вместо этого я скажу, что моя Ruby-реализация Wolfram Mathematica FoldList [] как часть драгоценного камня "mll" здесь может быть упрощено для цели OP при переходе:

def fold_list array
  start = 0
  Enumerator.new do |e|
    array.each do |i|
      e << start += i
    end
  end
end

irb> fold_list([1,2,3]).to_a
=> [1, 3, 6]

Ответ 5

Еще один подход (хотя я предпочитаю khell)

(1..10).inject([]) { |cs, i| cs << i + (cs.last || 0) }

После публикации ответа я увидел ответ, отправленный hrnt. Хотя два подхода выглядят одинаково, решение выше более эффективно, поскольку один и тот же массив используется в каждом цикле инъекции.

a,r = [1, 2, 3, 4],[]
k = a.inject(r) { |x, y| x + [(x.last || 0) + y] }
p r.object_id 
#  35742260
p k.object_id
#  35730450

Вы заметите, что r и k разные. Если вы выполните тот же тест для решения выше:

a,r = [1, 2, 3, 4],[]
k = a.inject(r) { |cs, i| cs << i + (cs.last || 0) }
p r.object_id
# 35717730
p k.object_id
# 35717730

Идентификатор объекта для r и k один и тот же.

Ответ 6

unearthing это для еще одного подхода, что изменяет массив на месте

class Array
  def cumulative_sum!
    (1..size-1).each {|i| self[i] += self[i-1] }
    self
  end
end

также может быть обобщено:

  def cumulate!( &block )
    (1..size-1).each {|i| self[i] = yield self[i-1], self[i] }
    self
  end

  >> (1..10).to_a.cumulate! {|previous, next| previous * next }
  => [1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]

Ответ 7

Попробуйте этот код

[1,2,3,4].inject([]){ |acc, value| acc << acc.last.to_i + value.to_i }

=> [1, 3, 6, 10]

Ответ 8

Функция Haskell, которую мы хотим, это scanl, а не scanr.

class Array
  def scanl(init)
    self.reduce([init]) { |a, e| a.push(yield(a.last, e)) }
  end
end

[1,2,3,4].scanl(0) { |a, b| a + b }[1..-1]
=> [1, 3, 6, 10]