Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Puts and times method on numbers

Tags:

ruby

This code:

puts 1.times { puts 2.times { puts 3.times { puts 4 } } }

Outputs this:

4 4 4 3 4 4 4 3 2 1

I would expect Ruby to output the return value of the times method, but it doesn't seem to do that. It prints out the number that times is being called on. What is happening?

like image 449
snowbillr Avatar asked Sep 14 '16 20:09

snowbillr


People also ask

How do you multiply using the box method?

To multiply two numbers using the box method, make a table with the place values of the first factor along the top and the place values of the second factor down the left. Multiply the place values and enter their products in the table. Simplify the table. Sum each row of the table.


3 Answers

You've got a (quasi) loop within a loop within a loop so Ruby's doing exactly what you're asking of it. The way these loops are evaluated is, generally speaking, from the inside out.

The return value from times is the number given in the first place, so 3.times returns 3.

With all that in mind you can interpret what's happening as this:

4 # First iteration of 3.times { puts 4 }
4
4
3 # Return value of 3.times displayed
4 # Second iteration of 3.times { puts 4 }
4
4
3 # Return value of 3.times displayed
2 # Return value of 2.times displayed
1 # Return value of 1.times displayed

Now when I see code like puts 3.times that's usually a mistake, same goes for puts array.each ... where someone probably means .map.

like image 191
tadman Avatar answered Nov 07 '22 09:11

tadman


I don't really get that output either, so let's dissect it, starting with the innermost expression:

puts 4
# 4           <- this should denote output
#=> nil       <- this should denote return value

It prints 4 and returns nil (puts always returns nil).

Wrapping it in 3.times { ... } prints 4 3 times:

3.times { puts 4 }
# 4
# 4
# 4
#=> 3

But instead of returning nil, it returns 3. This is because times always returns the receiver (i.e. the integer you call times on).

Now let's add another puts:

puts 3.times { puts 4 }
# 4
# 4
# 4
# 3
#=> nil

Same as above, but also prints the result of 3.times { ... }, i.e. 3.

Wrapping everything in 2.times { ... } duplicates the above output:

2.times { puts 3.times { puts 4 } }
# 4
# 4
# 4
# 3
# 4
# 4
# 4
# 3
#=> 2

It also returns 2 instead of nil because of 2.times { ... }.

Adding puts prints that 2:

puts 2.times { puts 3.times { puts 4 } }
# 4
# 4
# 4
# 3
# 4
# 4
# 4
# 3
# 2
#=> nil

Wrapping this in 1.times { ... } produces the same output, but changes the result from nil to 1:

1.times { puts 2.times { puts 3.times { puts 4 } } }
# 4
# 4
# 4
# 3
# 4
# 4
# 4
# 3
# 2
#=> 1

Adding the last puts prints that 1:

puts 1.times { puts 2.times { puts 3.times { puts 4 } } }
# 4
# 4
# 4
# 3
# 4
# 4
# 4
# 3
# 2
# 1
#=> nil
like image 45
Stefan Avatar answered Nov 07 '22 09:11

Stefan


I guess I'd expect Ruby to output the return value of the times method, but it doesn't seem to do that. It prints out the number that times is being called on.

That is the return value of the times method:

times {|i| block }self

like image 22
Jörg W Mittag Avatar answered Nov 07 '22 10:11

Jörg W Mittag