Array inject function problem

Hello Rubyists,
I am new to the language and the community.
My first problem is this :

i used the following code :

sum = 0
[1,3,5].inject() {|sum, element| sum + element}
print sum

I expected to get 9
but instead i get 4

I just installed the Ruby 1.8.6 one-click installer on windows
and wrote the above commands in SciTE.
Any suggestions??

On Fri, Apr 25, 2008 at 11:32 AM, Inbroker A. [email protected]
wrote:

I expected to get 9
but instead i get 4

I just installed the Ruby 1.8.6 one-click installer on windows
and wrote the above commands in SciTE.
Any suggestions??

You’re confused with how variables work in Ruby

puts [1,3,5].inject() {|sum, element| sum + element} # => 9

‘sum’ in the block is a different scope from your outside ‘sum’

Jason

On Friday 25 April 2008, Jason R. wrote:

puts [1,3,5].inject() {|sum, element| sum + element} # => 9

‘sum’ in the block is a different scope from your outside ‘sum’

Jason

That’s not true, at least with ruby 1.8:

sum = 0
[1,3,5].inject() {|sum, element| sum += element}
print sum
=> 9

In ruby 1.8, if a variable with the same name as a block argument
exists, it’s
used in place of the block variable. This changed in ruby 1.9, where a
new
variable is always created, shadowing the one outside the block, as you
stated.

To understand why

print sum

prints 4, it’s necessary to understand how exactly inject works:

  1. the accumulator (the first block parameter) is set either to the
    argument
    to inject or, if it’s not given (as in this case) to the first argument
    of
    the array
  2. for each element of the array (except the first, if no argument was
    given),
    the block is called and the accumulator is set to the value returned by
    the
    block. I guess this doesn’t happen for the last element: in this case,
    instead
    of setting the accumulator to the return value of the block, inject
    simply
    returns that value.

Usually, the accumulator is a block-local variable, and so it’s
discarded
after inject returns. In this case, instead, it’s a local variable
created
outside the block. When inject changes the accumulator, it changes that
local
variable, and this is the reason it returns 4.

The correct way to use inject is to use its return value, not it’s
accumulator:

res = [1,2,3].inject(){|sum, element| sum + element}
print res

I hope this helps

Stefano

On Apr 25, 2008, at 11:37 AM, Jason R. wrote:

print sum
puts [1,3,5].inject() {|sum, element| sum + element} # => 9

‘sum’ in the block is a different scope from your outside ‘sum’

Jason

Actually, in Ruby 1.8 the block variable sum “uses” the existing sum
in the outer scope. (Ruby 1.9 will change this.) When the block is
executed for the last time, sum is assigned 4 and element 5. The +
gives a value of 9, but the block isn’t executed after that to assign
to sum again.

You probably want the slightly changed:

sum = [1,3,5].inject(0) {|s,e| s+e}
puts sum

-Rob

Rob B. http://agileconsultingllc.com
[email protected]

On Fri, 25 Apr 2008 10:37:43 -0500, Jason R. wrote:

‘sum’ in the block is a different scope from your outside ‘sum’
This is the case in Ruby 1.9, and if it were the case here, he’d get 0,
not 4. He’s using 1.8, where sum in the block is the same sum as
outside the block, so let’s look at how this works.

sum=0
simple enough. this could easily be done without (since it will
just be overwritten), but then sum wouldn’t be accessable outside
the block.

[1,3,5].inject() {|sum, element| sum + element}

inject calls: yield 1,3
so the block runs assigning the variable sum to be 1
and the variable element to be 3. the block returns 4, but sum
is not updated with that value

now, inject calls: yield 4,5
(4 is the value that the block returned before)
so the block runs, assigning the variable sum to be 4 and
the variable element to be 5. the block returns 9.

seeing as how there’s no more values in the array, inject returns 9
but the block is not called again, so sum is not updated

puts sum
we print the last value of sum, which was 4

On Apr 25, 2008, at 12:20 PM, Inbroker A. wrote:

Thanks so much guys.
So the sum =[1,3,5].inject() {|sum, element| sum + element;}
runs on both 1.8 and 1.9 right???
1.9 Ruby is in beta? (haven’t seen it yet)
not on the official site neither on the netbeans 6.1 RC2 that i just
installed

Yes, but if the array is empty [] rather than 0 you’d get nil. If you
don’t want that behavior, provide the initial value for the
accumulator as an argument:

sum = [].inject(0) {|sum, element| sum + element}

gives sum == 0

-Rob

Rob B. http://agileconsultingllc.com
[email protected]

On Fri, 25 Apr 2008 11:20:29 -0500, Inbroker A. wrote:

Thanks so much guys.
So the sum =[1,3,5].inject() {|sum, element| sum + element;} runs on
both 1.8 and 1.9 right???

Yes, but it’s best to use a different variable name inside the block.

1.9 Ruby is in beta? (haven’t seen it yet) not on the official site
neither on the netbeans 6.1 RC2 that i just installed

Ruby 1.9.0 is a feature-frozen development release of Ruby 1.9, and it
was released on December 25th. I think they’re still working on
implementation bugs.

http://www.ruby-lang.org/en/news/2007/12/25/ruby-1-9-0-released/

Thanks so much guys.
So the sum =[1,3,5].inject() {|sum, element| sum + element;}
runs on both 1.8 and 1.9 right???
1.9 Ruby is in beta? (haven’t seen it yet)
not on the official site neither on the netbeans 6.1 RC2 that i just
installed

Inbroker A. wrote:

Hello Rubyists,
I am new to the language and the community.
My first problem is this :

i used the following code :

sum = 0
[1,3,5].inject() {|sum, element| sum + element}
print sum

I expected to get 9
but instead i get 4

I just installed the Ruby 1.8.6 one-click installer on windows
and wrote the above commands in SciTE.
Any suggestions??

Don’t ever use inject–forget you ever read about it. Use a hand
written loop of your own instead. Your code will be easier to
understand and it will be more efficient.

Don’t ever use inject–forget you ever read about it. Use a hand
written loop of your own instead. Your code will be easier to
understand and it will be more efficient.

,----[ /tmp/inject.rb ]
| require ‘benchmark’
|
| s = {}
| range = 1…10_000
|
| Benchmark.bm(8) do | b |
| b.report(‘inject:’) { s[:inject] = range.inject(0) { | sum, n | sum + n } }
| b.report(‘loop:’) do
| sum = 0
| for n in range
| sum += n
| end
| s[:loop] = sum
| end
| b.report(‘math:’) { s[:math] = ((range.max + 1) * range.max) / 2 - ((range.min - 1) * range.min) / 2 }
| end
|
| p s
`----

$ ruby1.8 /tmp/inject.rb
user system total real
inject: 0.130000 0.030000 0.160000 ( 0.155900)
loop: 0.060000 0.010000 0.070000 ( 0.064232)
math: 0.110000 0.010000 0.120000 ( 0.128134)
{:loop=>50005000, :math=>50005000, :inject=>50005000}

$ ruby1.9 /tmp/inject.rb
user system total real
inject: 0.020000 0.000000 0.020000 ( 0.020228)
loop: 0.060000 0.000000 0.060000 ( 0.063856)
math: 0.000000 0.000000 0.000000 ( 0.000150)
{:loop=>50005000, :math=>50005000, :inject=>50005000}

You are right for Ruby 1.8 (ruby 1.8.5 (2006-08-25) [i486-linux] here),
even math is slower (Bignum I think). But for Ruby 1.9 you are
wrong.

inject is the feature I liked most in Smalltalk.

mfg, simon … l

So let me ask you this:

going back to

sum = [1,3,5].inject(0) {|s,e| s+e}
puts sum

what if you want to return an array that shows each step of the way,
such that:

sum => [1,4,9]

??

Jason L. wrote:

sum => [1,4,9]

??
irb(main):003:0> steps = []
=> []
irb(main):004:0> sum = [1,3,5].inject(0) {|s,e| steps << s+e;
steps.last}
=> 9
irb(main):005:0> p steps
[1, 4, 9]

Hi –

On Sat, 26 Apr 2008, 7stud – wrote:

written loop of your own instead. Your code will be easier to
understand and it will be more efficient.

inject isn’t really hard to understand; it’s just an iterator with a
kind of feedback loop into the first block parameter. If you find it a
bit tricky I’d still encourage you to study it and accustom yourself
to it. It’s awfully useful.

David

On Wed, Jun 11, 2008 at 5:50 PM, Tim H. [email protected] wrote:

that:
[1, 4, 9]
Here’s a way to do it without the extra variable:

[1,3,5].inject([]) {|s,e| s = s.to_a; s << e + ( s.last || 0)}

or if you still want to inject 0 as the starting value:

[1,3,5].inject(0) {|s,e| s = s.to_a; s << e + s.last}[1…-1]

Now these will give a deprecation warning since Ruby 1.9 does away with
Object#to_a, an alternative is:

[1,3,5].inject(0) {|s,e| s = [s].flatten; s << e + s.last}[1…-1]


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

On Thu, Jun 12, 2008 at 9:15 AM, Sebastian H.
[email protected] wrote:

Rick DeNatale wrote:

[1,3,5].inject([]) {|s,e| s = s.to_a; s << e + ( s.last || 0)}

If you’re okay with 0 being inside the result array you can also do:
[1,3,5].inject([0]) {|s, e| s << s.last + e}

and if you are not

[1,3,5].inject([0]){| a, e| a<< a.last + e}[1…-1]

:wink:
Robert

Hi –

On Thu, 12 Jun 2008, Rick DeNatale wrote:

=> 9
[1,3,5].inject(0) {|s,e| s = s.to_a; s << e + s.last}[1…-1]

Now these will give a deprecation warning since Ruby 1.9 does away with
Object#to_a, an alternative is:

[1,3,5].inject(0) {|s,e| s = [s].flatten; s << e + s.last}[1…-1]

And yet another way:

[1,3,5].inject([0,[]]) {|(i,acc),e| [i+e, acc << i+e]}[1]

I suspect each_cons could get involved there somewhere…

David

On Thu, Jun 12, 2008 at 3:27 AM, Robert D. [email protected]
wrote:

[1,3,5].inject([0]){| a, e| a<< a.last + e}[1…-1]
Which I had in my original offering before Sebastien trimmed it.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

On Thu, Jun 12, 2008 at 1:38 PM, Rick DeNatale [email protected]
wrote:

[1,3,5].inject([0]){| a, e| a<< a.last + e}[1…-1]

Which I had in my original offering before Sebastien trimmed it.
I only steal from the best of course!!!
Sorry Rick must have lost it somewhere :frowning:
Cheers
Robert

Rick DeNatale wrote:

[1,3,5].inject([]) {|s,e| s = s.to_a; s << e + ( s.last || 0)}

If you’re okay with 0 being inside the result array you can also do:
[1,3,5].inject([0]) {|s, e| s << s.last + e}

HTH,
Sebastian