2009-09-25 32 views
22

¿Cuál es el más hábil, forma más Rubí-como el cálculo de la suma acumulada de una matriz?acumulativa matriz suma en Ruby

Ejemplo:

[1,2,3,4].cumulative_sum 

deberían volver

[1,3,6,10] 
+0

volviendo a esta pregunta yo mismo, ¡apenas 5,5 años después! – Peter

Respuesta

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

o simplemente 'suma + = x', supongo. – Peter

+0

sí, lo actualicé: P – khelll

9

Esta es una manera

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

Si está bien que la respuesta es más de una sentencia, entonces esto sería limpiador:

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] 

También podríamos tener una nota de Haskell y hacer una versión de rubí 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 para la referencia al escáner, eso es lo que es una inyección acumulada. Tenga en cuenta que puede agregar scanr al módulo Enumerable en lugar de simplemente a la clase Array. – tokland

1

Un enfoque más (aunque prefiero Khell 's)

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

vi la respuesta Publicado por hrnt después de publicar mi respuesta. Aunque dos enfoques tienen el mismo aspecto, solución anterior es más eficaz como la misma matriz se utiliza en cada ciclo de inyección.

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 

Notarás que r y k son diferentes. Si hace la misma prueba para la solución anterior:

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 

La identificación del objeto para r y k es la misma.

+1

Se puede modificar a cs.last.to_i también, no más corto, pero ¿quizás más legible? – TCSGrad

2

También se puede leer sobre scanl - característica que necesita, pero que aún no está implementada en Ruby, por lo que yo sé. Éstos son ejemplos y fuente de ejemplo de código de la misma: http://billsix.blogspot.com/2008/11/functional-collection-patterns-in-ruby.html

UPD: El enlace de arriba está muerto, así que en vez voy a decir que mi aplicación Rubí de Wolfram Mathematica de FoldList[] como parte de joya "mll" here puede simplificarse para el propósito de OP mientras que ser Enumerable:

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

El enlace está roto (404). – amoebe

+0

Sí (hay muchos enlaces pero está muerto. Espero que alguien encuentre el espejo. – Nakilon

1

desenterrar este enfoque para uno más, que modifica la matriz en el lugar

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

también se puede generalizar:

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

probar este código

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

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

La función Haskell que queremos es scanl, no 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] 
Cuestiones relacionadas