1 Updating
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
.[1] 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
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.[2] 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.
- This sentence is worded very carefully, and it is important to understand why. Note that it does not say "it adds the value 1 to the value in the memory location identified by
age
". It is the assignment operator, not the addition operator, that changes the value in the memory location identified byage
. ↵ - Remember, you can also use parentheses to influence the order in which operators are evaluated. For example, in this case, one could avoid any confusion by writing
price = price - (0.25 * price);
This is a question of style and doesn't change the pattern. ↵