Enumerable.inject is pretty cool, but it treat the elements as if they
have
the same weight when doing the calculation, in my case I need to apply a
different weight based on the index of each element, therefore I also
need
the index being provided as one more block parameter.
enum.find_index might help, but I think there should be a more direct
way to
get the index.
In short, like for enum.each_entry, there’s each_with_index,
similarly, for enum.inject, is it possible to have
enum.inject_with_index {
|memo, obj, index| … } ?
Enumerable.inject is pretty cool, but it treat the elements as if they have
the same weight when doing the calculation, in my case I need to apply a
different weight based on the index of each element, therefore I also need
the index being provided as one more block parameter.
enum.find_index might help, but I think there should be a more direct way to
get the index.
In short, like for enum.each_entry, there’s each_with_index,
similarly, for enum.inject, is it possible to have enum.inject_with_index {
|memo, obj, index| … } ?
I’m curious as to when and why people use each_with_index and/or
inject, etc, instead of a somewhat less elegant, and admittedly
possibly less flexible, more direct approach. (Which usually(?) takes
about the same number of characters for the code.)
ary = [3, 4, 5]
rrr = ary.each_with_index.inject(0) { |sum, (v, index)| sum + v * index
}
p rrr #=> 14
index = -1; rrr = ary.inject(0) { |sum, v| index += 1; sum + v * index }
p rrr #=> 14
rrr = 0; ary.each_with_index { |v, index| rrr += v * index }; rrr
p rrr #=> 14
index = -1; rrr = 0; ary.each { |v| index += 1; rrr += v * index }; rrr
p rrr #=> 14
require “benchmark”
kt = 500_000
Benchmark.bmbm() do |bm|
bm.report( “each_with_index.inject(0)” ) { kt.times {
rrr = ary.each_with_index.inject(0) { |sum, (v, index)| sum + v *
index }
} }
bm.report( “inject(0)” ) { kt.times {
index = -1; rrr = ary.inject(0) { |sum, v| index += 1; sum + v *
index }
} }
bm.report( “each_with_index” ) { kt.times {
rrr = 0; ary.each_with_index { |v, index| rrr += v * index }
} }
bm.report( “each” ) { kt.times {
index = -1; rrr = 0; ary.each { |v| index += 1; rrr += v * index }
} }
end
I was working with array, so I can write
[3, 4, 5].each_with_index.inject(0) { |sum, (value, index)| sum + value
*
index } #=> 14
OR
ary = [3, 4, 5]
ary.each_index.inject(0) { |sum, index| sum + ary[index] * index } #=>
14
works like a charm
botp, Thank you very much!
BTW, one more finding is that,
puts([3, 4, 5].each_with_index.inject(0) do |sum, (value, index)| sum +
value * index end)
puts([3, 4, 5].each_with_index.inject(0) { |sum, (value, index)| sum +
value
index })
puts [3, 4, 5].each_with_index.inject(0) { |sum, (value, index)| sum +
value
index }
puts [3, 4, 5].each_with_index.inject(0) do |sum, (value, index)| sum +
value * index end
all works but the last one, it reports TypeError: 0 is not a symbol,
actually caused by the block being passed to puts instead of inject,
then
without a block, inject expect a symbol as argument. hum, just a note to
myself.
As you said, the code you demonstrated, could performer better,
meanwhile
it’s somewhat less elegant.
IMHO the code is ok, we just have more space inside the
interpreter/runtime to get improved, like in JVM, the JIT compiler could
compile all of the 4 lines of code in to the same machine instructions
and
yield the same performance.
The dynamic nature and elegance design of Ruby certainly sacrificed
performance at somewhat level, but that doesn’t stop you and me from
using
it, right?
Anyway, thanks for the benchmark demo, I just ran it on my machine, with
both MRI and JRuby,
MRI 1.8.7
user system total real
each_with_index.inject(0) 4.930000 0.000000 4.930000 ( 5.036288)
inject(0) 3.088000 0.000000 3.088000 ( 3.080176)
each_with_index 2.668000 0.000000 2.668000 ( 2.668153)
each 1.700000 0.000000 1.700000 ( 1.713098)
JRuby 1.5.6
user system total real
each_with_index.inject(0) 1.918000 0.000000 1.918000 ( 1.918000)
inject(0) 1.058000 0.000000 1.058000 ( 1.058000)
each_with_index 0.979000 0.000000 0.979000 ( 0.979000)
each 0.624000 0.000000 0.624000 ( 0.624000)
Like your result the raw each runs the fastest, but we can also find
that,
the result of each_with_index.inject with JRuby is already close to that
of
raw each with MRI.
I’m curious as to when and why people use each_with_index and/or
inject, etc, instead of a somewhat less elegant, and admittedly
possibly less flexible, more direct approach.
I think you answered it yourself: people will generally choose the more
elegant and/or clearer solution (by their own assessment of ‘clearer’).
Unless this is the innermost loop of a program which processes a large
dataset, and you’ve used a profiler to prove that this is the hotspot in
your particular application, the performance difference doesn’t matter a
hoot.
The dynamic nature and elegance design of Ruby certainly sacrificed
each 1.700000 0.000000 1.700000 ( 1.713098)
raw each with MRI.
rrr = ary.each_with_index.inject(0) { |sum, (v, index)| sum + v * index }
Benchmark.bmbm() do |bm|
index = -1; rrr = 0; ary.each { |v| index += 1; rrr += v * index }
I’m curious as to when and why people use each_with_index and/or
inject, etc, instead of a somewhat less elegant, and admittedly
possibly less flexible, more direct approach.
I think that might be the very definition of preemptive optimization.
Make it work first, then make it fast… if you need to.
This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.