Inject does not inject last value

Hi All,

inject is a powerful method in ruby. but the ff gives me surprise…

irb(main):001:0> sum=0
=> 0
irb(main):002:0> [1,2,3,4,5].inject{|sum,e| sum+e }
=> 15
irb(main):003:0> sum
=> 10
irb(main):004:0>
irb(main):006:0> RUBY_VERSION
=> “1.8.5”
irb(main):007:0> prod=1
=> 1
irb(main):008:0> [1,2,3,4,5].inject(1){|prod,e| prod*e }
=> 120
irb(main):009:0> prod
=> 24

it seems that inject does not update accumulator (sum or prod in
examples) on last iteration.

tested on windows and linux.

kind regards

Its not a bug, its because thats what sum/prod are on the last
iteration.
sum=0
[1,2,3,4,5].inject{|sum,e| sum+e }
Is:
(sum=0)+1
(sum=1)+2
(sum=3)+3
(sum=6)+4
(sum=10)+5

If you did:
sum=0
[1,2,3,4,5].inject{|sum,e| sum+=e }
Then sum would be 15, but thats not how inject should be used.
Whats wrong with:
sum = [1,2,3,4,5].inject{|sum,e| sum+e } ?

j`ey
http://www.eachmapinject.com

irb(main):001:0> sum = [1,2,3,4,5].inject{|sum,e| sum+e}
=> 15
irb(main):002:0> sum
=> 15

fr joey:

Whats wrong with:

sum = [1,2,3,4,5].inject{|sum,e| sum+e } ?

dry.
i still think inject should update sum before exit.

kind regards -botp

Gregor K. schrieb:

irb(main):001:0> sum = [1,2,3,4,5].inject{|sum,e| sum+e}
=> 15
irb(main):002:0> sum
=> 15

First you see the Return Value (15), but bevor assign the return value
to sum sum will be 10 and not the return value of the operation itself.
i cannot teach, im a dumpass english loser.

Hm…

irb(main):001:0> a = 0
=> 0
irb(main):002:0> a + 1
=> 1
irb(main):003:0> a
=> 0
irb(main):004:0> a = a + 1
=> 1
irb(main):005:0> a
=> 1

no. it matters if you get the return value or assign a value.

Peña schrieb:

Peña schrieb:

fr joey:

Whats wrong with:

sum = [1,2,3,4,5].inject{|sum,e| sum+e } ?

dry.

aha, i didnt get the message of fr joey here.
very strange.

fr joey:

Whats wrong with:

sum = [1,2,3,4,5].inject{|sum,e| sum+e } ?

dry.
i still think inject should update sum before exit.

To me sum = [1,2,3,4,5].inject{|sum,e| sum+e } is more DRY

than:
sum =0
[1,2,3,4,5].inject{|sum,e| sum+e }

Regards,
Rimantas

On Aug 7, 2006, at 12:36 AM, Peña, Botp wrote:

Hi All,

inject is a powerful method in ruby. but the ff gives me surprise…

irb(main):001:0> sum=0
=> 0
irb(main):002:0> [1,2,3,4,5].inject{|sum,e| sum+e }
=> 15
irb(main):003:0> sum
=> 10

Inject is just an iterator:

% echo “sum=0; [1,2,3,4,5].inject{|sum,e| sum+e }” | parse_tree_show -f

[[:lasgn, :sum, [:lit, 0]],
[:iter,
[:call,
[:array, [:lit, 1], [:lit, 2], [:lit, 3], [:lit, 4], [:lit, 5]],
:inject],
[:masgn,
[:array,
[:lasgn, :sum], # <<<<<
[:dasgn_curr, :e]]],
[:call, [:lvar, :sum], :+, [:array, [:dvar, :e]]]]]

Because sum was assigned initially outside of the inject, it is an
lvar (local variable) instead of a dvar (dynamic/iter var, like e).
You’ll notice that the assignments to both of those variables happens
before the call, and is part of the iteration mechanics itself. There
is no assignment done at the end of an iteration.

I think it is more important to point out that inject is
just_another_iterator. There is nothing special about it or how it
works. It is just a simple each just like everything else in
Enumerable. This makes the ruby implementation cleaner and easier to
maintain. Sum isn’t an accumulator, as much as it is just another
variable. Should map or reject have some special semantics attached
to their block variables?

If you really are stuck on this idea, you can always change “sum+e”
to read “sum+=e” but at that stage, why use an inject at all?

Here is our implementation from metaruby:

def inject(memo = :_nothing)
enum = self.to_a.dup

 memo = enum.shift if memo == :_nothing

 return memo if enum.empty?

 enum.each do |item|
   memo = yield memo, item
 end
 return memo

end

fr Ryan:
[snipped cool samples]

Because sum was assigned initially outside of the inject, it is an

lvar (local variable) instead of a dvar (dynamic/iter var, like e).

You’ll notice that the assignments to both of those variables

happens

before the call, and is part of the iteration mechanics

itself. There

is no assignment done at the end of an iteration.

I think it is more important to point out that inject is

just_another_iterator. There is nothing special about it or how it

works. It is just a simple each just like everything else in

Enumerable. This makes the ruby implementation cleaner and easier to

maintain. Sum isn’t an accumulator, as much as it is just another

variable. Should map or reject have some special semantics attached

to their block variables?

If you really are stuck on this idea, you can always change “sum+e”

to read “sum+=e” but at that stage, why use an inject at all?

[snipped code explanation]

Hi Ryan, thank you for the very detailed and enlightening explanation. I
apologize for being too dumb on this inject stuff ().

kind regards -botp

fr Robert:

No, just the fact that you name a variable “sum” doesn’t imply

semantics. In your code “sum” is just a block variable. And it’s

yes, Robert, that was my most dangerous practice, associating local vars
with block vars. I hope to cut the bad practice asap. Ryan just gave a
tech explanation, too…

thanks and kind regards -botp

Peña wrote:

fr joey:

Whats wrong with:

sum = [1,2,3,4,5].inject{|sum,e| sum+e } ?

dry.
i still think inject should update sum before exit.

No, just the fact that you name a variable “sum” doesn’t imply
semantics. In your code “sum” is just a block variable. And it’s
updated each time the block is invoked. So the last time the block is
invoked “e” has the value of the last element and “sum” equals the sum
of all preceding arguments. You should rather do this in order to avoid
confusion.

sum = [1,2,3,4,5].inject {|s,x| s+x}

If you want to stick with your original code where you predeclared “sum”
there is absolutely no point in using #inject. In that case #each is
much better:

sum = 0
(1…5).each {|x| sum += x}

An additional note: if you want to be your code to safe for empty
collections you should use this form

sum = enum.inject(0) {|s,x| s+x}

Kind regards

robert