Help me understand how this block works?


#1

Hello -

I’m working through a tutorial and Ive come across a line of code that I
do not understand. The program basically takes a roman number value and
converts it to its integer counterpart.

Here is the line:
roman_string.to_s.upcase.split(//).reverse.inject(0) do |memo, digit|

If roman_string contains the value “IV”, when we come to this line can
you please explain what the ‘inject’ does? When I step through it in
debug mode, I can see it gets assigned ‘0’. But how is the ‘digit’
populated? How does it know it gets the ‘V’ first and subsequently ‘I’?
Does it do this by default because the inject(0) populates the memo so
the only variable left is digit?

I tried to play around, by removing the ‘.inject(0)’ and the ‘memo’ to
see if I could understand what it was doing but to no avail.

roman_string.to_s.upcase.split(//).reverse. do |digit| # program balks
at this

If you’d like me to post the whole program I could, but I didnt want to
clutter the post.

Thank you.
Brian A.


#2

On 30.11.2008, at 18:27 , Brian A. wrote:

Posted via http://www.ruby-forum.com/.

Google “ruby iterators tutorial” and try typing

ri Enumerable#inject

in the terminal.

einarmagnus


#3

Brian A. removed_email_address@domain.invalid wrote:

Here is the line:
roman_string.to_s.upcase.split(//).reverse.inject(0) do |memo, digit|

If roman_string contains the value “IV”, when we come to this line can
you please explain what the ‘inject’ does? When I step through it in
debug mode, I can see it gets assigned ‘0’. But how is the ‘digit’
populated? How does it know it gets the ‘V’ first and subsequently ‘I’?
Does it do this by default because the inject(0) populates the memo so
the only variable left is digit?

Just look at the docs:

http://ruby-doc.org/core-1.8.7/classes/Enumerable.html

Scroll down to “inject” and look at the third format. The “0” from the
original call is the first value passed to “memo”; meanwhile, the
elements of the array are the values passed each time to “digit”.

The “digit” population goes in the order “V” then “I” because the string
has been reversed with the “reverse” method.

The hard part of “inject” to understand is not either of those; it is
the matter of the subsequent values of “memo”. It is the result of
each previous execution of the block. (There is no “previous execution
of the block” the first time, obviously, so we need to settle on the
first value in some other way. Here, that way is the specification of
the “0” parameter.)

The example in the docs is a very nice one:

(5…10).inject(1) {|product, n| product * n } #=> 151200

If you can see why the final output of the block is the multiplicative
product of all the numbers in the enumerable, you’ve understood
“inject”. The “memo” (here called “product”) is seeded with “1” because
multiplying any value by “1” gives the same value, so we get the right
answer for the first enumerable and then we’re off to the races.

So, our “product” and “n” values each time thru the loop are:

1, 5
15, 6
1
56, 7
1
567, 8
15678, 9
15678*9, 10

Finally, the last time through, we produce 15678910 and stop.

If you apply that kind of reasoning to your example, you’ll understand
it!

m.


#4

Einar Magnús Boson removed_email_address@domain.invalid wrote:

try typing

  ri Enumerable#inject

in the terminal.

Except that on my machine, the result is:

Big-iMac-Attack:~ mattneub$ ri Enumerable#inject
------------------------------------------------------ Enumerable#inject
inject(init) {|result, item| …}

 From /usr/local/share/ri/1.8/site/Enumerable/cdesc-Enumerable.yaml

 [no description]

It looks like my core ri data has been overridden by my standard library
ri data. Or something. m.


#5

On Sun, Nov 30, 2008 at 12:27 PM, Brian A.
removed_email_address@domain.invalid wrote:

you please explain what the ‘inject’ does? When I step through it in
debug mode, I can see it gets assigned ‘0’. But how is the ‘digit’
populated? How does it know it gets the ‘V’ first and subsequently ‘I’?
Does it do this by default because the inject(0) populates the memo so
the only variable left is digit?

It populates memo with 0 at first and thereafter with the value that
the block returns on each iteration. digit is simply the value of
each element in turn, which, in this case, is each character in the
string that is in #reverse. The value of the block (memo on the next
iteration) is the last statement executed within that block. Some
people don’t like the method name #inject, but I do, because I think
of it as “injecting” the block result back into the block on each
iter. Learn #each and #map first, then tackle #inject.

The most common example is using it for summing…

a = [1, 5, 9, 7]
a.inject(0) {|s, i| s + i}
#=> 22

…if you don’t supply the initial 0 the memo will take on the first
object of the enumerable object. So if I did…

a.inject {|s, i| s + i}

…it would still work for me, but not for you, because in your roman
numeral case, your first object would be a String.

I tried to play around, by removing the ‘.inject(0)’ and the ‘memo’ to
see if I could understand what it was doing but to no avail.

roman_string.to_s.upcase.split(//).reverse. do |digit| # program balks

“do” is not a method, it’s a keyword, so you can’t just use “do” on
something that doesn’t except a block. That line is trying to use
“do” with no block accepting method.

hth,
Todd


#6

matt neuburg wrote:

Just look at the docs:

http://ruby-doc.org/core-1.8.7/classes/Enumerable.html

Scroll down to “inject” and look at the third format. The “0” from the
original call is the first value passed to “memo”; meanwhile, the
elements of the array are the values passed each time to “digit”.

The “digit” population goes in the order “V” then “I” because the string
has been reversed with the “reverse” method.

The hard part of “inject” to understand is not either of those; it is
the matter of the subsequent values of “memo”. It is the result of
each previous execution of the block. (There is no “previous execution
of the block” the first time, obviously, so we need to settle on the
first value in some other way. Here, that way is the specification of
the “0” parameter.)

The example in the docs is a very nice one:

(5…10).inject(1) {|product, n| product * n } #=> 151200

If you can see why the final output of the block is the multiplicative
product of all the numbers in the enumerable, you’ve understood
“inject”. The “memo” (here called “product”) is seeded with “1” because
multiplying any value by “1” gives the same value, so we get the right
answer for the first enumerable and then we’re off to the races.

So, our “product” and “n” values each time thru the loop are:

1, 5
15, 6
1
56, 7
1
567, 8
15678, 9
15678*9, 10

Finally, the last time through, we produce 15678910 and stop.

If you apply that kind of reasoning to your example, you’ll understand
it!

m.

Thank you, that explains it very well for me. Another quick question.
Would it be possible to inject to two different vars?

For instance if something like (syntax is probably wrong if its even
possible):

(5…10).inject(1).inject(2) {|product, variable_two, n| product *
variable_two * n}


#7

Hi –

On Mon, 1 Dec 2008, Brian A. wrote:

Thank you, that explains it very well for me. Another quick question.
Would it be possible to inject to two different vars?

For instance if something like (syntax is probably wrong if its even
possible):

(5…10).inject(1).inject(2) {|product, variable_two, n| product *
variable_two * n}

If the collection you’re iterating through has pairs of values, you
can do this:

[[1,2], [3,4], [5,6]].inject(0) {|acc, (x,y)| acc + x + y }

Kind of pointless here, since I’m just adding them up, but that
technique might help you.

David


#8

On Sun, Nov 30, 2008 at 1:13 PM, Brian A.
removed_email_address@domain.invalid wrote:

Thank you, that explains it very well for me. Another quick question.
Would it be possible to inject to two different vars?

You apply #inject on an Enumerable object. Do you mean that you want
to iterate over two arrays at the same time?

For instance if something like (syntax is probably wrong if its even
possible):

(5…10).inject(1).inject(2) {|product, variable_two, n| product *
variable_two * n}

I can’t tell what you are trying to accomplish here, but your
Enumerable can hold any object (like Arrays, etc.)

Todd


#9

Brian A. wrote:

can
you please explain what the ‘inject’ does?

There was a recent thread which may provide some useful examples: around
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/320755


#10

Todd B. wrote:

I can’t tell what you are trying to accomplish here, but your
Enumerable can hold any object (like Arrays, etc.)

Todd

Not trying to accomplish anything, just trying to understand how it
works (Im just learning Ruby so I apologize for any nonsensical
questions).

My last question is because the block is parsing through my string. In
this case we are passing it roman value ‘IV’, why does it inject only on
the first pass? I would of expected it to inject on every pass… like
more of a loop?

Thank you again.


#11

Brian C. wrote:

Brian A. wrote:

can
you please explain what the ‘inject’ does?

There was a recent thread which may provide some useful examples: around
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/320755

Excellent, that answers it perfectly thank you.