Chapter 2: Variables and Operators

2.1 More printing

We saw a simple “Hello, world” program in the previous chapter. It used a puts statement (puts "Hello, world.") to output a message. Generically speaking, a puts statement is known as a print statement. Whether or not it contains the word “print,” a print statement sends some output to the user’s screen or to a file. This is another instance of computer scientists using words differently than the general population. Most people think of printing as sending something to the computer’s printer. More and more you are learning to think like a computer scientist.

You can put as many statements as you want in a program. For example, to print more than one line:

# generate some simple output

puts "Hello, world." # print one line
puts "How are you?"  # print another

Also, as you can see, it is legal to put comments at the end of a line, as well as on a line by themselves.

The phrases that appear in quotation marks are called strings, because they are made up of a sequence (string) of letters. Actually, strings can contain any combination of letters, numbers, punctuation marks, and other special characters.

After each line, puts adds a special character, called a newline, that causes the cursor to move to the next line of the display. The next time puts is invoked, the new text appears on the next line.

Often it is useful to display the output from multiple print statements all on one line. You can do this with the print command, which does not add a newline:

# generate some simple output

print "Goodbye, "
puts "cruel world!"

In this case the output appears on a single line as Goodbye, cruel world!. Notice that there is a space between the word “Goodbye” and the second quotation mark. This space appears in the output string, so it affects the behavior of the program.

2.2 Whitespace

Spaces, tabs, and newlines are collectively known as whitespace. Spaces and tabs that appear in your source code outside of quotation marks generally do not affect the behavior of the program. For example, I could have written:

  print "Goodbye, "
puts    "cruel world!"

This program would run just as well as the original. If I separate the statements with a semicolon, the breaks at the ends of lines (newlines) do not affect the program’s behavior, either. Hence, I could have written:

print "Goodbye, ";puts "cruel world!"

That would work, too, although you have probably noticed that the program is getting harder to read. Whitespace is useful for organizing your program visually, making it easier to read the program and locate syntax errors.

2.3 Variables

One of the most powerful features of a programming language is the ability to manipulate variables. A variable is a named location in memory that stores a value. Values are things that can be printed and stored and (as we’ll see later) operated on. The strings we have been printing ("Hello, World.", "Goodbye, ", etc.) are values.

The value of a variable has a type that determines what operations can be performed with it, what the programmer can do with it. For example, the integers in a Ruby program have type Integer, Fixnum or Bignum; you can do arithmetic with them. It will probably come as no surprise that the strings have type String; one thing you can do with a String is center it. Types in Ruby always begin with a capital letter.

Ruby comes with several built-in types available for your use. The “Classes” section of the Ruby Class and Library Reference lists the built-in types on the left. Some of what can be done with each type is listed on the right. (For now, you can think of “class” as the same thing as “type.”) In Chapter what, we’ll see how you can define your own types. (Later I need to explain duck typing and maybe dynamic typing.)

In general, you will want to make up variable names that indicate what you plan to do with the variable. For example, if you see these variable names in a program: first_name, last_name, hour, minute, you can probably make a good guess at what types of values are stored in them. The first_name and last_name variables probably store Strings. The hour and minute variables probably store some type of number, such as 9 and 45. On the other hand, hour and minute might store Strings: they might store "9" and "45"! It all depends on what the programmer plans to do with the values.

In general, variable names in Ruby can contain lowercase letters, uppercase letters, numerals, and underscores. A variable name cannot contain spaces. If you want a variable name of more than one word, place an underscore between the words, as in first_name. For now I’ll tell you, Ruby insists that the variable name begin with a lowercase letter.

2.4 Assignment

We store a value in a variable with an assignment statement.

fred = "Hello." # give fred the value "Hello."
hour = 11       # assign the value 11 to hour
minute = 59     # set minute to 59

The above example shows three assignments, and the comments show three different ways people sometimes talk about assignment statements. The vocabulary can be confusing here, but the idea is straightforward: when you make an assignment to a variable, you give it a value.

A common way to represent variables on paper is to draw a box with the name of the variable on the outside and the value of the variable on the inside. This figure shows the effect of the three assignment statements:

Assigning values to variables

For each variable, the name of the variable appears outside the box and the value appears inside.

One source of confusion is that some strings look like integers, but they are not. For example, fred can contain the string "123", which is made up of the characters 1, 2 and 3, but that is not the same thing as the number 123.

fred = "123" # legal
fred = 123   # also legal, but different

I pulled a fast one on you in the code above. Notice in the first statement that I assigned a value to fred. In a later statement—the next one, in fact—I assigned a different value to fred. This is called reassignment. One moment fred contained the value "123", and some time later it contained a different value, the number 123. fred continues to contain the number 123 until a later reassignment (or until fred goes out of scope, which we’ll cover in Chapter what).

2.5 Printing variables

You can print the value of a variable using the same commands we used to print Strings.

first_line = "Hello, again!"
puts first_line

This program creates a variable named first_line, assigns it the value "Hello, again!" and then prints that value. When we talk about “printing a variable,” we mean printing the value of the variable. To print the name of a variable, you precede it with a colon. For example, puts :first_line

If you want to get a little tricky, you could write

first_line = "Hello, again!"
print "The value of first_line is "
puts first_line

The output of this program is

The value of first_line is Hello, again!

I am pleased to report that the syntax for printing a variable is the same regardless of the variable’s type.

hour = 11
minute = 59
print "The current time is "
print hour
print ":"
print minute
puts "."

The output of this program is

The current time is 11:59.

2.6 Keywords

A few sections ago, I said that you can make up any name you want for your variables, but that’s not quite true. There are certain words that are reserved in Ruby because they are used by the interpreter to parse the structure of your program, and if you use them as variable names, it will get confused. These words, called keywords or reserved words, include if, while, def, end, class, and many more.

The complete list of reserved words is available at Zen Spider’s Ruby QuickRef. Though the page is incomplete overall, it includes very handy reminders of how to do what in Ruby. The QuickRef will make more sense as you become more familiar with Ruby.

Rather than memorize the list, I suggest that you take advantage of a feature provided in many text editors for programmers: color syntax highlighting. As you type, different parts of your program should appear in different colors. For example, keywords might be blue, strings red, comments green, and other code black. If you type a variable name and it turns blue, watch out! You might get some strange behavior from the Ruby interpreter.

Some Ruby words are so frequently used that, although they are not keywords, you should not use them for your own variable names. So far we’ve seen puts and print. I’ve colored them purple in the code snippets in this book. These words might or might not turn color in your text editor. (They don’t in mine.) If you use puts or print as a variable name, not only will other people reading your code get confused, but you will likely have a hard time printing your variables. And when you read your own code a few weeks later, you’ll get confused, too! We’ll see other important Ruby words as the book progresses.

2.7 Interactive Ruby

Now we’re going to switch gears and use Ruby interactively. So far, you’ve been writing Ruby code in a text file and invoking the interpreter to run your code from start to finish. If you think about it a bit, you should realize that your Ruby program will produce no output unless it contains output statements such as puts and print. Interactive Ruby is different. When using the interactive Ruby interpreter, irb, you immediately see the result of each Ruby statement you type. That is its power and raison d’être (reason for existing).

Your installation of Ruby should have come with the irb program. Open a command line window, type irb (or perhaps irb.exe on a Windows machine), and hit the return key. Now you’re ready to roll. Here is how irb looks when I start it on my Macintosh:

[pongal:~] elizabeth% irb
irb(main):001:0>

The angle bracket (and the stuff to the left of it) is the prompt. You can enter any Ruby statement after the prompt. irb will immediately display the result of executing that statement. Then it prompts you for another statement.

irb is the perfect place to perform small Ruby experiments. I even use irb as a calculator. We’ll use irb to experiment with Ruby’s operators. To quit irb at any time, type the exit command and hit return.

2.8 Operators

Operators are special symbols that are usually used to represent simple computations like addition and multiplication. Most of the operators in Ruby do exactly what you would expect them to do, for most of them are common mathematical symbols. For example, the operator for adding two numbers is +. The operator for multiplying two numbers is *, not the symbol x.

The following are all legal Ruby expressions whose meaning is more or less obvious:

1+1
hour-1
hour*60 + minute
minute/60

Expressions can contain both variable names and numbers. In each case, the name of the variable is replaced with its value before the computation is performed.

Addition, subtraction and multiplication all do what you expect, but you might be surprised by division. For example, take a look at this irb session:

irb(main):001:0> hour = 11
11
irb(main):002:0> minute = 59
59
irb(main):003:0> "Number of minutes since midnight:"
Number of minutes since midnight:
irb(main):004:0> hour*60 + minute
719
irb(main):005:0> "Fraction of the hour that has passed:"
Fraction of the hour that has passed:
irb(main):006:0> minute/60
0

The first calculation produces what we expected, 719. But the second calculation is strange: the value of the variable minute is 59, and 59 divided by 60 is 0.98333, not 0. The reason for the discrepancy is that Ruby is performing integer division.

When both of the operands are integers (operands are the things operators operate on), the result must also be an integer, and by convention integer division always rounds down, even in cases like this where the next integer is so close.

A possible alternative in this case is to calculate a percentage rather than a fraction:

irb(main):007:0> "Percentage of the hour that has passed:"
Percentage of the hour that has passed:
irb(main):008:0> minute*100/60
98

Again the result is rounded down, but at least now the answer is approximately correct. In order to get an even more accurate answer, we could use a different type of variable, called floating-point, that is capable of storing fractional values. We’ll get to that in the next chapter.

2.9 Order of operations

When more than one operator appears in an expression, the order of evaluation depends on the rules of precedence. A complete explanation of precedence can get complicated, but just to get you started:

The “Operators and Precedence” section of the Ruby QuickRef lists all the operators and their precedence. Don’t worry if much of the list looks puzzling now.

2.10 Operators for Strings

In general you cannot perform mathematical operations on Strings, even if the strings look like numbers. The following are illegal (if we know that fred’s value is a String):

irb(main):001:0> fred = "postmodern"
"postmodern"
irb(main):002:0> fred - 1
NameError: undefined method `-' for "postmodern":String
        from (irb):27
irb(main):003:0> "Hello"/123
NameError: undefined method `/' for "Hello":String
        from (irb):28
irb(main):004:0> fred * "Hello"
TypeError: no implicit conversion from string
        from (irb):29:in `*'
        from (irb):29

Interestingly, the + operator does work with Strings, although it does not do exactly what you might expect. For Strings, the + operator represents concatenation, which means joining up the two operands by linking them end-to-end. So "Hello, " + "world." yields the string "Hello, world." and fred + "ism" adds the suffix ism to the end of whatever fred is, which is often handy for naming new movements or forms of bigotry:

irb(main):005:0> fredism = fred + "ism"
"postmodernism"
irb(main):006:0> fredism
"postmodernism"
irb(main):007:0> fred
"postmodern"

Notice the concatenate operator does not change the value of fred. Rather, using the concatenate operator returns a fresh String.

The * operator works with Strings provided the second operand is a number, not another String. The * operator returns a fresh String containing a number of copies of the original String:

irb(main):008:0> fred * 3
"postmodernpostmodernpostmodern"
irb(main):009:0> (fred + " ") * 3
"postmodern postmodern postmodern "
irb(main):010:0> fred
"postmodern"

Several other operators work with Strings, but here I’d like to tell you about the << operator, the append operator. The append operator concatenates the value of a second String onto the end of the first String. This operator is different from the others in that it changes the first operand’s value. Let’s see it in action:

irb(main):011:0> suffix = "ism"
"ism"
irb(main):012:0> fred << suffix
"postmodernism"
irb(main):013:0> fred << "s"
"postmodernisms"
irb(main):014:0> fred
"postmodernisms"
irb(main):015:0> suffix
"ism"

Notice that the append operator changes the value of fred but not the value of suffix. You can use the append operator to build a String in several steps, as shown above.

2.11 Composition

So far we have looked at the elements of a programming language—variables, expressions, and statements—in isolation, without talking about how to combine them.

One of the most useful features of programming languages is their ability to take small building blocks and compose them. For example, we know how to multiply numbers and we know how to print; it turns out we can do both at the same time:

puts 17 * 3

Actually, I shouldn’t say “at the same time,” since in reality the multiplication has to happen before the printing, but the point is that any expression, involving numbers, strings, and variables, can be used inside a print statement. We’ve already hinted at this in one of the irb examples. Let’s look at some code:

hour = 11
minute = 59
puts hour * 60 + minute

But you can also put arbitrary expressions on the right-hand side of an assignment statement:

percentage = (minute * 100) / 60

This ability may not seem so impressive now, but we will see other examples where composition makes it possible to express complex computations neatly and concisely.

WARNING: There are limits on where you can use certain expressions; most notably, the left-hand side of an assignment statement has to be a variable name, not an expression. That’s because the left side indicates the storage location where the result will go. Expressions do not represent storage locations, only values. So the following is illegal: minute+1 = hour.

2.12 Tim Towtdi and interpolation

Who is Tim Towtdi? Nobody, really. “Tim Towtdi” is how to pronounce the acronym TIMTOWTDI: “There Is More Than One Way To Do It,” a philosophy Ruby inherits from a programming language called Perl.

Suppose we know that the variable message contains the string value "Ruby rocks!". And suppose we want to produce this output:

Listen up: Ruby rocks!

Reviewing the chapter, we can think of more than one way to produce this output using the message variable:

message = "Ruby rocks!"

# a) Step-by-step using print and puts
print "Listen up: "
puts message

# b) String concatenation
puts "Listen up: " + message

# c) String append operator
output = "Listen up: "
output << message
puts output

Each way has its own pros and cons, and each way is more or less useful in a given situation.

There is yet another way to produce the desired output: expression interpolation. If you type the variable name as part of the string and enclose the variable name with #{}, the value of the variable is converted to a String and inserted into the string at that spot. Say what? I’ll try that again. Within the string, the sequence #{expression} is replaced by the value of expression. Really, it’s easier than it sounds.

message = "Ruby rocks!"
puts "Listen up: #{message}"

The above code produces the desired output. Cool, huh?

We saw in Section 2.5 how to produce

The current time is 11:59.

given that the variables hour and minute contain integer values. We used a laborious procedure involving several print statements and a puts. Interpolation is the perfect solution for producing a combination of string and numerical output:

hour = 11
minute = 59
puts "The current time is #{hour}:#{minute}."

Besides the value of a variable, you can even interpolate calculations within the curly braces:

minute = 59
puts "Percentage of the hour that has passed: #{minute*100/60}"

The arithmetic is performed and the result is interpolated into the String to produce the following output:

Percentage of the hour that has passed: 98

NOTE: Interpolation works only if the string is surrounded by double-quote marks (""), not single-quote marks ('', i.e., apostrophes). That is why I have been getting you accustomed to typing double-quote marks around all your strings.

2.13 Glossary

print statement:
Any statement that outputs text. So far, the print statements we have seen are puts and print.
string:
A sequence of characters. A Ruby string is of type String.
newline:
A character (or characters in a Windows system) that marks the end of a line of code or output.
whitespace:
Characters that look white when printed on white paper: spaces, newlines, and tabs.
variable:
A named storage location for values.
value:
A number or string (or other thing to be named later) that can be stored in a variable. Every value has a type.
type:
A set of values. The type of a value determines what can be done with it. So far, the types we have seen are integers (Integer or Fixnum in Ruby) and strings (String in Ruby).
assignment:
A statement that assigns a value to a variable.
reassignment:
Using the same variable on the left-hand side of more than one assignment statement.
keyword:
A reserved word that is used by the interpreter to parse programs. You cannot use keywords, like while, def and end as variable names.
text editor:
A simple word processor. Text editors designed for computer programmers apply color syntax highlighting to the code on the screen, like the code examples you see in this book. The colors are not saved in the file; only the text is.
irb:
The interactive Ruby interpreter.
operator:
A special symbol that represents a simple computation like addition, multiplication or string concatenation.
integer division:
The operation that is performed when both operands of a division operator are integers. Integer division rounds the result down to the nearest integer value.
operand:
One of the values on which an operator operates.
precedence:
The order in which operations are evaluated.
concatenate:
To join two operands end-to-end.
append:
To concatenate the value of a the second operand onto the end of the first operand. Appending changes the value of the first operand.
statement:
A line of code that represents a command or action. So far, the statements we have seen are assignments and print statements.
expression:
A combination of variables, operators and values that represents a single result value. Expressions also have types, as determined by their operators and operands. In Ruby, an expression on a line of its own is also a statement.
composition:
The ability to combine simple expressions and statements into compound statements and expressions in order to represent complex computations concisely.
TIMTOWTDI:
A Ruby philosophy: “There is more than one way to do it.”
expression interpolation:
Substituting the value of an expression in a Ruby string.
GNU Free Documentation License Valid HTML Valid CSS Built with BBEdit
Friendly links for Google “juice”