Array inject function problem

I

Inbroker Adams

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??
 
J

Jason Roelofs

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??

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
 
R

Rob Biedenharn

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??

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


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 Biedenharn http://agileconsultingllc.com
(e-mail address removed)
 
S

Stefano Crocco

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??

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

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
 
K

Ken Bloom

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??

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'

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
 
I

Inbroker Adams

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
 
R

Rob Biedenharn

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 Biedenharn http://agileconsultingllc.com
(e-mail address removed)
 
K

Ken Bloom

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/
 
7

7stud --

Inbroker said:
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.
 
S

Simon Krahnke

* 7stud -- said:
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
 
D

David A. Black

Hi --

Inbroker said:
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.

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

--
Rails training from David A. Black and Ruby Power and Light:
INTRO TO RAILS June 9-12 Berlin
ADVANCING WITH RAILS June 16-19 Berlin
INTRO TO RAILS June 24-27 London (Skills Matter)
See http://www.rubypal.com for details and updates!
 
J

Jason Lillywhite

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]

??
 
T

Tim Hunter

Jason said:
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]

??
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]
 
R

Rick DeNatale

[Note: parts of this message were removed to make it a legal post.]

Jason said:
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]

??
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]


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 = .flatten; s << e + s.last}[1..-1]
 
D

David A. Black

Hi --

Jason said:
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]

??
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]


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 = .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

--
Rails training from David A. Black and Ruby Power and Light:
INTRO TO RAILS June 9-12 Berlin
ADVANCING WITH RAILS June 16-19 Berlin
ADVANCING WITH RAILS July 21-24 Edison, NJ
See http://www.rubypal.com for details and updates!
 
S

Sebastian Hungerecker

Rick said:
[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
 
R

Robert Dober

Rick said:
[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]

;)
Robert
 
R

Rick DeNatale

[Note: parts of this message were removed to make it a legal post.]

Rick said:
[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]


Which I had in my original offering before Sebastien trimmed it. <G>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,792
Messages
2,569,639
Members
45,348
Latest member
RoscoeNevi

Latest Threads

Top