Books / Ruby for Beginners / Chapter 10

Code Blocks in Ruby

Ruby has its own definition of code block. Usually when we look at a program, we can visually separate blocks or chunks of code. For example, first 3 lines are responsible for user input, next 5 lines for output, and so on. Even though we can call these lines blocks of code from purely visual point of view, there are code blocks in Ruby, and they have special meaning.

Code block in Ruby is a part of a program that we pass somewhere (to function), so it will get executed under some circumstances. One wonders, why do we need to pass it while we can execute the block right away? Actually passing a code block to a function makes sense in the following cases:

  • This code needs to be executed certain amount of times. For example, we want to show the message “Boston Red Sox Are World Champions!” 10 times. Instead of just using puts ten times, we can take advantage of Ruby code blocks and pass the block to some function (we’ll discover how to do it later in this chapter). In this case program can be written in one line instead of ten.
  • Depending on some conditions code can be executed or not. And sometimes decision isn’t up to a programmer, there could be some circumstances. In other words, if you see a code block, it doesn’t mean that it will be executed for sure.

Syntax for code blocks in Ruby can be done in two ways:

  • If block is large and it takes multiple lines, you should use keywords do and end.
  • If it’s only one line, you can use curly braces: { and }

If you use this or another syntax, result will be the same. Curly braces work great for simple expressions. And between do and end we can keep as many lines as we want. Keep in mind that usually it’s not recommended to have huge code blocks.

Let’s try to use code blocks and see what happens:

$ irb
> 10.times { puts 'Boston Red Sox Are World Champions!' }
Boston Red Sox Are World Champions!
Boston Red Sox Are World Champions!
Boston Red Sox Are World Champions!
Boston Red Sox Are World Champions!
Boston Red Sox Are World Champions!
Boston Red Sox Are World Champions!
Boston Red Sox Are World Champions!
Boston Red Sox Are World Champions!
Boston Red Sox Are World Champions!
Boston Red Sox Are World Champions!
...

Let’s take a closer look. What is 10? Which class we’re dealing with? Yes, we’re already familiar with this class, it is Integer. Lookup documentation for Integer class and scroll down to “times” method. We can see that method “accepts” block. Actually, you can pass a block to any method, even to the method that doesn’t accept any block, because it’s up to the method to execute this block or not. Method “times” always executes the block.

So what do we have now? We have object 10, and this object “knows” about its state, internally there is a number 10 somewhere. There is method “times” implemented by Ruby language developer, and this method executes this block 10 times.

Remember that you can pass a block to any method, then it’s just up to the method what to do with the block. And if you want to know what is going to happen, you should lookup documentation. For example, the following code is perfectly valid:

gets { puts 'OK' } 

There won’t be any error, but this program makes no sense! Method “gets” doesn’t know what to do with a block, and it will be ignored.

Here is how you can pass multi-line block to “times” method:

10.times do
  puts "Boston Red Sox Are World Champions!"
  puts "(and New York Yankees too)"
end

Run this program on your computer and see what happens. And here is outline of what is happening above:

  • There is object 10 of class Integer
  • We’re calling method “times” on this object
  • We’re passing the code block to method “times”. This code block has 2 lines.

Author’s story: when I was about 8, my father showed me the first program written with BASIC language:

10 PRINT "Roman ";
20 GOTO 10

10 and 20 are line numbers, and there is infinite loop. Program prints the name and does this again, and again. Because there is no line feed, entire screen fills with exactly the same string, and visually all of it shifts to the left. You can do the same trick with Ruby:

loop do
  print "Roman "
end

Program above has infinite loop. print method is similar to puts, but the only difference is that there is no line feed (no new line).

Blocks With Parameters

Object that executes your block can pass a parameter. It depends on implementation, some objects do pass parameters to your blocks, others don’t. It’s up to a programmer if you want to use this parameter or want to ignore it. So far, we’ve been always ignoring this information, but technically parameter was always there. Let’s see how we can use it.

In case below we have object of 24 with type Integer. Let’s write a program called “Piggy Bank”, this piggy bank will accept 500 dollars every month, and will save this money. What we want to know is how much money it has after 24 months.

sum = 0

24.times do |n|
  sum = sum + 500
  puts "Month #{n}, piggy bank has #{sum} dollars"
end

Result:

Month 0, piggy bank has 500 dollars
Month 1, piggy bank has 1000 dollars
Month 2, piggy bank has 1500 dollars
Month 3, piggy bank has 2000 dollars
...
Month 22, piggy bank has 11500 dollars
Month 23, piggy bank has 12000 dollars

Well, result was little bit unpredictable. For some reason the count starts from zero: “Month 0”. Actually, it was expected, and the only confusing part is the variable naming. Usually “n” means “natural number”, and natural numbers can be used for counting1 (one apple, two apples, three apples, …). But in our case n can be 0, and it can confuse some programmers.

Usually one uses n, m, etc. for natural numbers. And if we’re talking about index, it starts with zero, and the more appropriate name for variable will be i, j, and so on. It’s not a mistake if you named variable incorrectly (and definition of correctness is only up to you), but the code has two readers: computer and human. And human is not only you, but also somebody else: your colleagues, friends, random people online who came across your published code, and also the future you. That’s why you need to write the most predictable code you can. Moreover, Ruby is about least surprise, and community expects the same from other Ruby developers.

With this in mind we can rewrite the program:

sum = 0

24.times do |i|
  sum = sum + 500
  puts "Month #{i}, piggy bank has #{sum} dollars"
end

But renaming variable is not enough, months still start from 0:

Month 0, piggy bank has 500 dollars
...

We don’t count apples starting from zero, and practically speaking there is no too much sense in counting months starting from zero. So let’s take advantage of string interpolation and replace expression i with i + 1:

sum = 0

24.times do |i|
  sum = sum + 500
  puts "Month #{i + 1}, piggy bank has #{sum} dollars"
end

Now it works as expected:

Month 1, piggy bank has 500 dollars
...
Month 24, piggy bank has 12000 dollars

For the sake of experiment imagine you have not just a piggy bank, but “Magic Piggy Bank, model 10”! It will generate the revenue of 10% every month for any money you put in:

sum = 0

24.times do |i|
  sum = sum + 500 + sum * 0.1
  puts "Month #{i + 1}, magic piggy bank has #{sum}"
end

So the improvement is only to add “+ sum * 0.1” on line 4, and here is result:

Month 1, magic piggy bank has 500.0
Month 2, magic piggy bank has 1050.0
Month 3, magic piggy bank has 1655.0
Month 4, magic piggy bank has 2320.5
...
Month 23, magic piggy bank has 39771.512162761865
Month 24, magic piggy bank has 44248.66337903805

In other words, Magic Piggy Bank will generate $44248 instead of just saved $12000.

Exercise 1 You’re planning to buy a house for $500.000 and your employer credit union is offering zero percent interest rate for 30 years. To pay off this house you need to pay $16.666 every year (it can be calculated by dividing $500K by 30). Write a program which will show the amount left to pay for each year.

Exercise 2 Modify the program from exercise 1 to meet the following criteria. Your interest rate is 4% a year for remaining amount. For every year calculate how much interest in dollars you need to pay for using bank’s money.

  1. However, some definitions begin natural numbers with 0. This situation is strange because mathematics is normally a very precise science and there is normally broad agreement about such definitions. 


Licenses and Attributions


Speak Your Mind

-->