Books / Patterns for Beginning Programmers / Chapter 2
Updating Variables
Every algorithm of any consequence makes extensive use of arithmetic operators, whether it is executed by hand or realized as a computer program and executed by a computer. However, the two are different in one very important way. Calculations that are performed by hand tend to progress down the page, using more and more memory (i.e., paper) as they proceed. Programs, on the other hand, tend to declare a small number of variables, assign values to them, and then assign updated values to them. This chapter considers different ways of updating variables.
Motivation
Suppose you are writing a program that keeps track of the age of your
favorite relative. You might declare an int
variable named age
and
assign your favorite relative’s current age to that variable.
Now, suppose you need to perform a calculation involving that relative’s
age next year. Obviously, you know that their age will be one greater
than it is now. But, should you create another variable for that value,
or should you just update the value of age
? In some situations you
need to do the former, but, suppose what you need to do is update the
existing variable. It turns out that there are a variety of different
ways that you can proceed.
Review
One approach to updating that you may have already seen involves the
increment and decrement operators, +
+
and -
-
, which
increase/decrease their operands by one. So, for example, you have
probably seen something like the following:
int age;
// Initialize the age to 0 at birth
age = 0;
// Increase the age by 1 on the first birthday
age++;
What you may not know is that, in some ways, this is just one (particularly simple) way of solving a specific updating problem (i.e., in which the variable is increased or decreased by exactly 1).
Thinking About The Problem
The same result can be achieved in a slightly more complicated but, ultimately, more flexible way. To understand how, first remember that the assignment operator takes the result of evaluating the expression on its right and puts it in the memory location identified by the variable on its left. This is done three different times in the following example:
int currentAge, increment, initialAge;
initialAge = 0;
increment = 1;
currentAge = initialAge + increment;
These three assignment statements are particularly easy to understand because the expression on the right side of the assignment operator does not involve the variable on the left side. However nothing about the syntax of assignment statements prevents this from being the case. For example, consider the following statement:
// Add age and 1 and assign the result to age
age = age + 1;
This statement first adds the value in the memory location identified by
age
and the value 1
and then assigns the result to the memory
location identified by age
. In other words, it does the same thing as
++age
.
To the non-programmer this statement looks like a mistake because the
non-programmer thinks that it says “age
equals age
plus one”, which
clearly can’t be true. However, that’s not what it says at all. It
actually says “add the value in the memory location identified by age
and the value one, and put the result in the memory location identified
by age
“.
The Pattern
This idea can be generalized in a variety of ways by recognizing that the important pattern is the presence of the left-side operand on the right side of the assignment operator. In a fairly abstract way, the pattern can be written (in pseudo-code) as follows:
value = value operator adjustment
where value
denotes the variable being updated, =
denotes the
assignment operator, operator
denotes a binary operator, and
adjustment
represents the “amount” of the adjustment. Since operator
has higher precedence than =
, it is evaluated first. Then, the result
of that evaluation (which involves value
) is assigned to value
.
This pattern is so common, that experienced programmers neither think about it themselves nor think to mention it to beginning programmers, but it’s not as obvious as everyone makes it out to be.
Examples of Updating Variables
You will encounter many situations in which you must keep track of something that is changing, but, regardless of its value, you want to use the same name/identifier. In the example above, you needed to keep track of a relative’s age over time, but you only needed their current age. In another program you may need to keep track of someone’s bank balance as it changes over time, but you only need the current balance. In still another program, you may need to keep track of the elevation of a highway as it changes over space, but you only need the elevation at one location.
A Gradebook Program
Suppose you have to write a program that manages the grades that a student receives in a course. After the initial grade is assigned, you must deduct the late penalty (which may, of course, be zero). You can implement this as follows:
// Assign the initial grade
grade = 85;
// Reduce a grade by a late penalty
grade = grade - latePenalty;
A Retail Sales Program
Now, suppose you have to write a program that offers frequent buyers a 25% discount when they check out. You could solve this problem as follows:
// Offer a 25% discount
price = price - 0.25*price;
Because the *
operator has the highest precedence, it is evaluated
first. Then, the result of the multiplication operation is subtracted
from the price
(without changing the contents of any of the
variables). Finally, the result of the subtraction operation is assigned
to the variable named price
.
Suppose that price
initially contains the value 40.0
. Then, this
statement can be visualized as in Figure 1.1.
A Banking Program
As one more example, suppose you have to write a program that updates an
account holder’s bank balance. Assuming an interest rate of 5%, the new
balance will equal the old balance plus 5% of the old balance You could
solve this problem as you did for the retail sales program, but you
could also do a little algebra “off line”, observe that
balance + 0.05 * balance
is equivalent to 1.05 * balance
, and
implement the solution as follows:
// Earn 5% interest
balance = 1.05 * balance;
A Warning
This pattern is so common that many programming languages include compound assignment operators to make it even easier to use. Such operators consist of multiple characters: the symbol for the arithmetic operator followed immediately by the symbol for the assignment operator. Note that, since a compound operator is an operator, it cannot contain white space (e.g., spaces, tabs, carriage returns, line feeds) between the characters. For example, the grading and banking examples can be written using compound assignment operators as follows:
// Reduce a grade by a late penalty
grade -= latePenalty;
// Earn 5% interest
balance *= 1.05;
Beginning programmers need to be a little careful when using compound assignment operators. To see why, consider the following two statements:
i =+ 1;
j =- 1;
While they look like they use compound assignment operators, they do not
— compound assignment operators end with the character that is used for
the assignment operator, they don’t start with it. That is, +=
is a
compound assignment operator, but =+
is not.
However, both of these statements are syntactically valid and, hence,
will compile. This is because they use the assignment operator (i.e.,
=
) followed be the unary “positive” (i.e., +
) or “negative” (i.e.,
-
) operators, without any white space between them. That is, they are
the same as the following two statements:
i = +1;
j = -1;
just with different spacing.