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

···

On 8/7/06, Peña, Botp <botp@delmonte-phil.com> 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
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

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

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

···

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

Gregor Kopp 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.

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

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 (<banging head>).

kind regards -botp

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.

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:

···

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

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

···

--
http://rimantas.com/

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

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