2009-09-25 4 views
22

配列の累積合計を計算する最も滑らかで、Rubyのような方法は何ですか?Rubyにおける累積配列の合計

例:

[1,2,3,4].cumulative_sum 

はここ

[1,3,6,10] 
+0

私自身、わずか5.5年後です! – Peter

答えて

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

か、単に 'sum + = x'だと思います。 – Peter

+0

はい、それを更新しました:P – khelll

9

を返す必要がありますが、それは答えが複数の文であることをOKであれば、これは次のようになり片道

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 
7
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] 
+0

+1をscanrに参照すると、それは蓄積された注入です。配列Arrayの代わりにEnumerableモジュールにscanrを追加できることに注意してください。 – tokland

1

もう一つのアプローチ(私はkhell年代を好むが)

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

私は私の答えを投稿した後hrntによって投稿答えを見ました。 2つのアプローチは同じように見えますが、各注入サイクルで同じアレイが使用されるため、上記のソリューションはより効率的です。

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のオブジェクトIDは同じです。

+1

cs.last.to_iに変更することもできますが、短くはないが読みやすいかもしれませんか? – TCSGrad

2

また、scanlについて読むことができます。必要な機能ですが、Rubyでは実装されていません。ここでは例と、それのサンプルソースコードです:http://billsix.blogspot.com/2008/11/functional-collection-patterns-in-ruby.html

UPD: 上記のリンクは死んでいるので、代わりに私は宝石の一部としてのWolfram MathematicaのFoldList[]のそれは私のRubyの実装「MLL」hereことができると言うだろう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] 
+0

リンクが切断されています(404)。 – amoebe

+0

ええ(そこにはたくさんのリンクがありますが、それは死んでいます。 – Nakilon

1

一の以上のアプローチのためにこれを発掘、アレイを修正することは、インプレース

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] 
1

は、私たちが望むHaskellの関数はscanl、ないscanrある

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

=> [1, 3, 6, 10] 
0

このコードを試してみてください。

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]