¿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]
¿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]
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
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]
+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
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.
Se puede modificar a cs.last.to_i también, no más corto, pero ¿quizás más legible? – TCSGrad
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]
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]
probar este código
[1,2,3,4].inject([]){ |acc, value| acc << acc.last.to_i + value.to_i }
=> [1, 3, 6, 10]
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]
volviendo a esta pregunta yo mismo, ¡apenas 5,5 años después! – Peter