From: Alex Fort Date: 2010-06-15T23:49:43+09:00 Subject: Re: Unexpected behavior in inject On Tue, Jun 15, 2010 at 10:35 AM, Josh Cheek wrote: > On Tue, Jun 15, 2010 at 9:21 AM, Alex Fort wrote: > >> I ran across something that puzzled me today, and I thought I'd ask here >> and >> see what you guys/girls think. I'm by no means a ruby guru, so if this is >> obvious to everyone but me, I'll take the learning experience. >> >> >> Here's the simple script I was testing out: >> >> [1,2,3].inject(0) do |acc, num| >>  acc + num >> end >> >> > Let me give you a slightly different example to show how it works:  You pass > in the zero, and it goes into acc in the block. Whatever the block returns > is then fed back into acc, for the next iteration. After the last iteration, > whatever the block returns is returned by the inject method. I think of it > as a number passing through an Enumerable and coming out the other side > modified. > > (5..10).inject 0 do |sum,num| >  sum + num > end > > This should return 45 in the following manner: > The first time the block is passed 0 , 5 and it returns 5 > The second time the block is passed 5 , 6 and it returns 11 > The third time the block is passed 11 , 7 and it returns 18 > The fourth time the block is passed 18 , 8 and it returns 26 > The fourth time the block is passed 26 , 9 and it returns 35 > The fourth time the block is passed 35 , 10 and it returns 45 > The method then returns 45 > > > > So why doesn't this work? > > [1,2,3].inject(0) do |acc, num| >>  acc + num >>  puts acc >> end >> > > Because the puts method returns nil. It is also the last thing in the block, > so the block returns nil. So nil is fed back into acc. Then nil + acc raises > the NoMethodError. > > To resolve this, think about where you could put the puts method that won't > cause the block to return nil. That's where I was going wrong, I didn't realize that inject passed the value of the *block*, back to the accumulator, not just the modified accumulator. I was thinking that ruby was somehow detecting when I modified the accumulator, so I was sometimes writing code like this: [1,2,3].inject(0) {|acc, num| acc += num} Now I get it. It's all about the value of the block, and there's really no magic going on. Insight is a wonderful thing :) > > > ----- > > If you are interested, here is a functional version (takes the enumerable as > a parameter) of inject that I wrote, except I call ti "passthrough" because > that is more meaningful for me. > > def passthrough( enumerable , to_pass ) >  enumerable.each do |element| >    to_pass = yield to_pass , element >  end >  to_pass > end > Thanks again, Alex