A fundamental feature of any programming language lies in its capability to make decisions. Decisions were made when executing the looping statements to determine when to terminate a loop. The Objective-C programming language also provides several other decision-making constructs, which are covered in this chapter:
• The if
statement
• The switch
statement
• The conditional
operator
if
StatementThe Objective-C programming language provides a general decision-making capability in the form of a language construct known as the if
statement. The general format of this statement is
if ( expression )
program statement
Imagine if you will that you could translate a statement such as “If it is not raining then I will go swimming” into the Objective-C language. Using the previous format for the if
statement, this might be “written” in Objective-C as follows:
if ( it is not raining )
I will go swimming
The if
statement is used to stipulate execution of a program statement (or statements if enclosed in braces) based on specified conditions. I will go swimming if it is not raining. Similarly, in the program statement
if ( count > MAXIMUM_SONGS )
[playlist maxExceeded];
the maxExceeded
message is sent to playlist
only if the value of count
is greater than the value of MAXIMUM_SONGS
; otherwise, it is ignored.
An actual program example will help drive the point home. Suppose you want to write a program that accepts an integer typed in from the terminal and then displays the absolute value of that integer. A straightforward way to calculate the absolute value of an integer is to simply negate the number if it is less than zero. The use of the phrase “if it is less than zero” in the previous sentence signals that a decision must be made by the program. This decision can be effected by the use of an if
statement as shown in the program that follows.
// Calculate the absolute value of an integer
#import <stdio.h>
int main (int argc, char *argv[])
{
int number;
printf ("Type in your number: ");
scanf ("%i", &number);
if ( number < 0 )
number = -number;
printf ("The absolute value is %i
", number);
return 0;
}
Type in your number: -100
The absolute value is 100
Type in your number: 2000
The absolute value is 2000
The program was run twice to verify that it is functioning properly. Of course, it might be desirable to run the program several more times to get a higher level of confidence so that you know it is indeed working correctly, but at least you know that you have checked both possible outcomes of the decision made by the program.
After a message is displayed to the user and the integer value that is entered is stored into number
, the program tests the value of number
to see whether it is less than zero. If it is, the following program statement, which negates the value of number
, is executed. If the value of number
is not less than zero, this program statement is automatically skipped. (If it is already positive, you don't want to negate it.) The absolute value of number is then displayed by the program, and program execution ends.
Let's look at another program that uses the if
statement. Let's add one more method to the Fraction
class, called convertToNum
. This method will provide the value of a fraction expressed as a real number. In other words, it will divide the numerator by the denominator and return the result as a double precision value. So, if you have the fraction 1/2, you want the method to return the value 0.5.
The declaration for such a method might look like this:
-(double) convertToNum;
And this is how you could write its definition:
-(double) convertToNum
{
return numerator / denominator;
}
Well, not quite. There are actually two serious problems with this method as it's defined. Can you spot them? The first has to do with arithmetic conversions. You will recall that numerator
and denominator
are both integer instance variables. So, what happens when you divide two integers? Correct, it is done as an integer division! So, if you wanted to convert the fraction 1/2, the previous code would give you zero! This is easily corrected by using the type cast operator to convert one or both of the operands to a floating-point value before the division takes place:
(double) numerator / denominator
Recalling the relatively high precedence of this operator, numerator
is first converted to double
before the division occurs. Further, you don't need to convert the denominator
because the rules of arithmetic conversion take care of that for you.
The second problem with this method is that you should check for division by zero (you should always check for that!). The invoker of this method could inadvertently have forgotten to set the denominator of the fraction or might have set the denominator of the fraction to zero, and you don't want your program to terminate abnormally.
The modified version of the convertToNum
method is shown here:
-(double) convertToNum
{
if (denominator != 0)
return (double) numerator / denominator;
else
return 0.0;
}
We arbitrarily decided to return 0.0 if the denominator
of the fraction is zero. Other options are available (such as printing an error message), but we won't go into them here.
Let's put this new method to use. Here's a test program to try it.
#import <stdio.h>
#import <objc/Object.h>
@interface Fraction: Object
{
int numerator;
int denominator;
}
-(void) print;
-(void) setNumerator: (int) n;
-(void) setDenominator: (int) d;
-(int) numerator;
-(int) denominator;
-(double) convertToNum;
@end
@implementation Fraction;
-(void) print
{
printf (" %i/%i ", numerator, denominator);
}
-(void) setNumerator: (int) n
{
numerator = n;
}
-(void) setDenominator: (int) d
{
denominator = d;
}
-(int) numerator
{
return numerator;
}
-(int) denominator
{
return denominator;
}
-(double) convertToNum
{
if (denominator != 0)
return (double) numerator / denominator;
else
return 0.0;
}
@end
int main (int argc, char *argv[])
{
Fraction *aFraction = [[Fraction alloc] init];
Fraction *bFraction = [[Fraction alloc] init];
[aFraction setNumerator: 1]; // 1st fraction is 1/4
[aFraction setDenominator: 4];
[aFraction print];
printf (" = ");
printf ("%g
", [aFraction convertToNum]);
[bFraction print]; // never assigned a value
printf (" = ");
printf ("%g
", [bFraction convertToNum]);
[aFraction free];
[bFraction free];
return 0;
}
1/4 = 0.25
0/0 = 0
After setting aFraction
to 1/4, the program uses the convertToNum
method to convert the fraction to a decimal value. This value is then displayed as 0.25
.
In the second case, bFraction
's value is not explicitly set, so its numerator and denominator are initialized to zero, which is the default for instance variables. This explains the result from the print
method. It also causes the if
statement inside the convertToNum
method to return the value 0
, as verified from the output.
if-else
ConstructIf someone asks you whether a particular number is even or odd, you will most likely make the determination by examining the last digit of the number. If this digit is either 0, 2, 4, 6, or 8, you will readily state that the number is even. Otherwise, you will claim that the number is odd.
An easier way for a computer to determine whether a particular number is even or odd is effected not by examining the last digit of the number to see whether it is 0, 2, 4, 6, or 8, but by simply determining whether the number is evenly divisible by 2. If it is, the number is even; otherwise, it is odd.
You have already seen how the modulus operator %
is used to compute the remainder of one integer divided by another. This makes it the perfect operator to use in determining whether an integer is evenly divisible by 2. If the remainder after division by 2 is 0, it is even; else, it is odd.
Now let's write a program that determines whether an integer value typed in by the user is even or odd and then displays an appropriate message at the terminal.
// Program to determine if a number is even or odd
#import <stdio.h>
int main (int argc, char *argv[])
{
int number_to_test, remainder;
printf ("Enter your number to be tested.: ");
scanf ("%i", &number_to_test);
remainder = number_to_test % 2;
if ( remainder == 0 )
printf ("The number is even.
");
if ( remainder != 0 )
printf ("The number is odd.
");
return 0;
}
Enter your number to be tested: 2455
The number is odd.
Enter your number to be tested: 1210
The number is even.
After the number is typed in, the remainder after division by 2 is calculated. The first if
statement tests the value of this remainder to see whether it is equal to zero. If it is, the message The number is even
is displayed.
The second if
statement tests the remainder to see if it's not equal to zero and, if that's the case, displays a message stating that the number is odd.
The fact is that whenever the first if
statement succeeds, the second one must fail, and vice versa. If you recall from our discussions of even/odd numbers at the beginning of this section, we said that if the number is evenly divisible by 2, it is even; else it is odd.
When writing programs, this “else” concept is so frequently required that almost all modern programming languages provide a special construct to handle this situation. In Objective-C, this is known as the if-else
construct, and the general format is as follows:
if ( expression )
program statement 1
else
program statement 2
The if-else
is actually just an extension of the general format of the if
statement. If the result of the evaluation of the expression is TRUE
, then program statement 1
, which immediately follows, is executed; otherwise, program statement 2
is executed. In either case, either program statement 1
or program statement 2
will be executed, but not both.
You can incorporate the if-else
statement into the previous program, replacing the two if
statements by a single if-else
statement. You will see how the use of this new program construct actually helps somewhat reduce the program's complexity and also improve its readability.
// Determine if a number is even or odd (Ver. 2)
#import <stdio.h>
int main (int argc, char *argv[])
{
int number_to_test, remainder;
printf ("Enter your number to be tested: ");
scanf ("%i", &number_to_test);
remainder = number_to_test % 2;
if ( remainder == 0 )
printf ("The number is even.
");
else
printf ("The number is odd.
");
return 0;
}
Enter your number to be tested: 1234
The number is even.
Enter your number to be tested: 6551
The number is odd.
Don't forget that the double equal sign (==
)is the equality test and the single equal sign is the assignment operator. It can lead to a lot of headaches if you forget this and inadvertently use the assignment operator inside the if
statement.
The if
statements you have used so far in this chapter set up simple relational tests between two numbers. Program 6.1 compared the value of number
against zero, whereas Program 6.2 compared the denominator of the fraction to zero. Sometimes it becomes desirable, if not necessary, to set up more sophisticated tests. Suppose, for example, you wanted to count the number of grades from an exam that were between 70 and 79, inclusive. In such a case, you would want to compare the value of a grade not merely against one limit, but against the two limits 70 and 79 to ensure that it fell within the specified range.
The Objective-C language provides the mechanisms necessary to perform these types of compound relational tests. A compound relational test is simply one or more simple relational tests joined by either the logical AND
or the logical OR
operator. These operators are represented by the character pairs &&
and ||
(two vertical bar characters), respectively. As an example, the Objective-C statement
if ( grade >= 70 && grade <= 79 )
++grades_70_to_79;
increments the value of grades_70_to_79
only if the value of grade
is greater than or equal to 70 and less than or equal to 79. In a like manner, the statement
if ( index < 0 || index > 99 )
printf ("Error - index out of range
");
causes execution of the printf
statement if index
is less than 0 or greater than 99.
The compound operators can be used to form extremely complex expressions in Objective-C. The Objective-C language grants the programmer ultimate flexibility in forming expressions, and this flexibility is a capability that is often abused. Simpler expressions are almost always easier to read and debug.
When forming compound relational expressions, liberally use parentheses to aid readability of the expression and avoid getting into trouble because of a mistaken assumption about the precedence of the operators in the or expression. (The &&
operator has lower precedence than any arithmetic or relational operator but higher precedence than the ||
operator.) Blank spaces should also be used to aid in the expression's readability. An extra blank space around the &&
and ||
operators will visually set these operators apart from the expressions that are being joined by these operators.
To illustrate the use of a compound relational test in an actual program example, let's write a program that tests to see whether a year is a leap year. We all know that a year is a leap year if it is evenly divisible by 4. What you might not realize, however, is that a year that is divisible by 100 is not a leap year unless it is also divisible by 400.
Try to think how you would go about setting up a test for such a condition. First, you could compute the remainders of the year after division by 4, 100, and 400 and assign these values to appropriately named variables, such as rem_4
, rem_100
, and rem_400
, respectively. Then you could proceed to test these remainders to determine whether the desired criteria for a leap year were met.
If we rephrase our previous definition of a leap year, we can say that a year is a leap year if it is evenly divisible by 4 and not by 100 or if it is evenly divisible by 400. Stop for a moment to reflect on this last sentence and to verify to yourself that it is equivalent to the previously stated definition. Now that we have reformulated our definition in these terms, it becomes a relatively straightforward task to translate it into a program statement as follows:
if ( (rem_4 == 0 && rem_100 != 0) || rem_400 == 0 )
printf ("It's a leap year.
");
The parentheses around the subexpression
rem_4 == 0 && rem_100 != 0
are not required because that is how the expression will be evaluated anyway, remembering that or &&
has higher precedence than ||
.
In fact, in this particular example, the test
if ( rem_4 == 0 && ( rem_100 != 0 || rem_400 == 0 ) )
would work just as well.
If you add a few statements in front of the test to declare the variables and to enable the user to key in the year from the terminal, you end up with a program that determines whether a year is a leap year, as shown here.
// This program determines if a year is a leap year
#import <stdio.h>
int main (int argc, char *argv[])
{
int year, rem_4, rem_100, rem_400;
printf ("Enter the year to be tested: ");
scanf ("%i", &year);
rem_4 = year % 4;
rem_100 = year % 100;
rem_400 = year % 400;
if ( (rem_4 == 0 && rem_100 != 0) || rem_400 == 0 )
printf ("It's a leap year.
");
else
printf ("Nope, it's not a leap year.
");
return 0;
}
Enter the year to be tested: 1955
Nope, it's not a leap year.
Enter the year to be tested: 2000
It's a leap year.
Enter the year to be tested: 1800
Nope, it's not a leap year.
The previous examples use a year that is not a leap year because it isn't evenly divisible by 4 (1955), a year that is a leap year because it is evenly divisible by 400 (2000), and a year that isn't a leap year because it is evenly divisible by 100 but not by 400 (1800). To complete the run of test cases, you should also try a year that is evenly divisible by 4 and not by 100. This is left as an exercise for you.
We mentioned that Objective-C gives the programmer a tremendous amount of flexibility in forming expressions. For instance, in the previous program, you did not have to calculate the intermediate results rem_4
, rem_100
, and rem_400
—you could have performed the calculation directly inside the if
statement, as follows:
if ( ( year % 4 == 0 && year % 100 != 0 ) || year % 400 == 0 )
The use of blank spaces to set off the various operators still makes the previous expression readable. If you decided to ignore adding blanks and removed the unnecessary set of parentheses, you could end up with an expression that looked like this:
if(year%4==0&&year%100!=0)||year%400==0)
This expression is perfectly valid and would (believe it or not) execute identically to the expression shown immediately before it. Obviously, those extra blanks go a long way toward aiding our understanding of complex expressions.
if
StatementsIn discussions of the general format of the if
statement, we indicated that if the result of evaluating the expression inside the parentheses were TRUE
, the statement that immediately followed would be executed. It is perfectly valid that this program statement be another if
statement, as in the statement
if ( [chessGame isOver] == NO )
if ( [chessGame whoseTurn] == YOU )
[chessGame yourMove];
If the value returned by sending the isOver
message to chessGame
is NO
, the following statement is executed, which is in turn another if
statement. This if
statement compares the value returned from the whoseTurn
method against YOU
. If the two values are equal, the yourMove
message is sent to the chessGame
object. Therefore, the yourMove
message is sent only if both the game is not done and it's your turn. In fact, this statement could have been equivalently formulated using compound relationals, like so:
if ( [chessGame isOver] == NO && [chessGame whoseTurn] == YOU )
[chessGame yourMove];
A more practical example of nested if
statements would be if you added an else
clause to the previous example, as shown in the following:
if ( [chessGame isOver] == NO )
if ( [chessGame whoseTurn] == YOU )
[chessGame yourMove];
else
[chessGame myMove];
Execution of this statement proceeds as described previously. However, if the game is not over and it's not your move, the else
clause is executed. This sends the message myMove
to chessGame
. If the game is over, the entire if
statement that follows, including its associated else
clause, is skipped.
Notice how the else
clause is associated with the if
statement that tests the value returned from the whoseTurn
method, and not with the if
statement that tests whether the game is over. The general rule is that an else
clause is always associated with the last if
statement that does not contain an else
.
You can go one step further and add an else
clause to the outermost if
statement in the preceding example. This else
clause would be executed if the game is over:
if ( [chessGame isOver] == NO )
if ( [chessGame whoseTurn] == YOU )
[chessGame yourMove];
else
[chessGame myMove];
else
[chessGame finish];
Of course, even if you use indentation to indicate the way you think a statement will be interpreted in the Objective-C language, it might not always coincide with the way the system actually interprets the statement. For instance, removing the first else
clause from the previous example
if ( [chessGame isOver] == NO )
if ( [chessGame whoseTurn] == YOU )
[chessGame yourMove];
else
[chessGame finish];
will not result in the statement being interpreted as indicated by its format. Instead, this statement will be interpreted as follows:
if ( [chessGame isOver] == NO )
if ( [chessGame whoseTurn] == YOU )
[chessGame yourMove];
else
[chessGame finish];
This is because the else
clause is associated with the last un-elsed if
. You could use braces to force a different association in those cases in which an innermost if
does not contain an else
but an outer if
does. The braces have the effect of closing off the if
statement. Thus,
if ( [chessGame isOver] == NO ) {
if ( [chessGame whoseTurn] == YOU )
[chessGame yourMove];
}
else
[chessGame finish];
achieves the desired effect.
else if
ConstructYou have seen how the else
statement comes into play when you have a test against two possible conditions—either the number is even, else it is odd; either the year is a leap year, else it is not. However, programming decisions you have to make are not always so black and white. Consider the task of writing a program that displays –1 if a number typed in by a user is less than zero, 0 if the number typed in is equal to zero, and 1 if the number is greater than zero. (This is actually an implementation of what is commonly called the sign function.) Obviously, you must make three tests in this case to determine whether the number that is keyed in is negative, zero, or positive. The simple if
-else
construct will not work. Of course, in this case, you could always resort to three separate if
statements, but this solution will not always work—especially if the tests that are made are not mutually exclusive.
You can handle the situation just described by adding an if
statement to your else
clause. We mentioned that the statement that follows an else
could be any valid Objective-C program statement, so why not another if
? Thus, in the general case, you could write the following:
if ( expression 1 )
program statement 1
else
if ( expression 2 )
program statement 2
else
program statement 3
This effectively extends the if
statement from a two-valued logic decision to a three-valued logic decision. You can continue to add if
statements to the else
clauses, in the manner just shown, to effectively extend the decision to an n-valued logic decision.
The preceding construct is so frequently used that it is generally referred to as an else if
construct and is usually formatted differently from that shown previously, like so:
if ( expression 1 )
program statement 1
else if ( expression 2 )
program statement 2
else
program statement 3
This latter method of formatting improves the readability of the statement and makes it clearer that a three-way decision is being made.
The next program illustrates the use of the else if
construct by implementing the sign function discussed earlier.
// Program to implement the sign function
#import <stdio.h>
int main (int argc, char *argv[])
{
int number, sign;
printf ("Please type in a number: ");
scanf ("%i", &number);
if ( number < 0 )
sign = -1;
else if ( number == 0 )
sign = 0;
else // Must be positive
sign = 1;
printf ("Sign = %i
", sign);
return 0;
}
Please type in a number: 1121
Sign = 1
Please type in a number: -158
Sign = -1
Please type in a number: 0
Sign = 0
If the number that is entered is less than zero, sign
is assigned the value -1
; if the number is equal to zero, sign
is assigned the value 0
; otherwise, the number must be greater than zero, so sign
is assigned the value 1
.
The next program analyzes a character that is typed in from the terminal and classifies it as either an alphabetic character (a–z or A–Z), a digit (0–9), or a special character (anything else). To read a single character from the terminal, the format characters %c
are used in the scanf
call.
// This program categorizes a single character
// that is entered at the terminal
#import <stdio.h>
int main (int argc, char *argv[])
{
char c;
printf ("Enter a single character:
");
scanf ("%c", &c);
if ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') )
printf ("It's an alphabetic character.
");
else if ( c >= '0' && c <= '9' )
printf ("It's a digit.
");
else
printf ("It's a special character.
");
return 0;
}
Enter a single character:
&
It's a special character.
Enter a single character:
8
It's a digit.
Enter a single character:
B
It's an alphabetic character.
The first test that is made after the character is read in determines whether the char
variable c
is an alphabetic character. This is done by testing whether the character is a lowercase letter or an uppercase letter. The former test is made by the following expression:
( c >= 'a' && c <= 'z' )
This expression is TRUE
if c
is within the range of characters 'a'
through 'z'
; that is, if c
is a lowercase letter. The latter test is made by this expression:
( c >= 'A' && c <= 'Z' )
This expression is TRUE
if c
is within the range of characters 'A'
through 'Z'
; that is, if c
is an uppercase letter. These tests work on computer systems that store characters inside the machine in a format known as ASCII.1
If the variable c
is an alphabetic character, the first if
test succeeds and the message It's an alphabetic character.
is displayed. If the test fails, the else if
clause is executed. This clause determines whether the character is a digit. Note that this test compares the character c
against the characters '0'
and '9'
and not the integers 0 and 9. This is because a character was read in from the terminal, and the characters '0'
to '9'
are not the same as the numbers 0–9. In fact, in ASCII, the character '0'
is actually represented internally as the number 48, the character '1'
as the number 49, and so on.
If c
is a digit character, the phrase It's a digit.
is displayed. Otherwise, if c
is not alphabetic and is not a digit, the final else
clause is executed and displays the phrase It's a special character
at the terminal. Execution of the program is then complete.
You should note that even though scanf
is used here to read just a single character, the Enter key must still be pressed after the character is typed to send the input to the program. In general, whenever you're reading data from the terminal, the program doesn't see any of the data typed on the line until the Enter key is pressed.
Let's suppose for the next example that you want to write a program that allows the user to type in simple expressions of the following form:
number operator number
The program will evaluate the expression and display the results at the terminal. The operators you want to have recognized are the normal operators for addition, subtraction, multiplication, and division. Let's use the Calculator
class from Program 4.6 in Chapter 4, “Data Types and Expressions,” here. So, each expression will be given to the calculator for computation.
The following program uses a large if
statement with many else if
clauses to determine which operation is to be performed.
// Program to evaluate simple expressions of the form
// number operator number
// Implement a Calculator class
#import <objc/Object.h>
#import <stdio.h>
@interface Calculator: Object
{
double accumulator;
}
// accumulator methods
-(void) setAccumulator: (double) value;
-(void) clear;
-(double) accumulator;
// arithmetic methods
-(void) add: (double) value;
-(void) subtract: (double) value;
-(void) multiply: (double) value;
-(void) divide: (double) value;
@end
@implementation Calculator;
-(void) setAccumulator: (double) value
{
accumulator = value;
}
-(void) clear
{
accumulator = 0;
}
-(double) accumulator
{
return accumulator;
}
-(void) add: (double) value
{
accumulator += value;
}
-(void) subtract: (double) value
{
accumulator -= value;
}
-(void) multiply: (double) value
{
accumulator *= value;
}
-(void) divide: (double) value
{
accumulator /= value;
}
@end
int main (int argc, char *argv[])
{
double value1, value2;
char operator;
Calculator *deskCalc = [[Calculator alloc] init];
printf ("Type in your expression.
");
scanf ("%lf %c %lf", &value1, &operator, &value2);
[deskCalc setAccumulator: value1];
if ( operator == '+' )
[deskCalc add: value2];
else if ( operator == '-' )
[deskCalc subtract: value2];
else if ( operator == '*' )
[deskCalc multiply: value2];
else if ( operator == '/' )
[deskCalc divide: value2];
printf ("%.2f
", [deskCalc accumulator]);
[deskCalc free];
return 0;
}
Type in your expression.
123.5 + 59.3
182.80
Type in your expression.
198.7 / 26
7.64
Type in your expression.
89.3 * 2.5
223.25
The scanf
call specifies that three values are to be read into the variables value1
, operator
, and value2
. A double
value can be read in with the %lf
format characters. This is the format used to read in the value of the variable value1
, which is the first operand of the expression.
Next, you read in the operator. Because the operator is a character ('+'
, '-'
, '*'
, or '/'
) and not a number, you read it into the character variable operator. The %c
format characters tell the system to read in the next character from the terminal. The blank spaces inside the format string indicate that an arbitrary number of blank spaces are to be permitted on the input. This enables you to separate the operands from the operator with blank spaces when you type in these values.
After the two values and the operator have been read in, the program stores the first value into the calculator's accumulator. Next, you test the value of operator
against the four permissible operators. When a correct match is made, the corresponding message is sent to the calculator to perform the operation. In the last printf
, the value of the accumulator is retrieved for display. Execution of the program is then complete.
A few words about program thoroughness are in order at this point. Although the preceding program does accomplish the task that we set out to perform, the program is not really complete because it does not account for mistakes made on the part of the user. For example, what would happen if the user were to type in a ?
for the operator by mistake? The program would simply fall through the if
statement and no messages would ever appear at the terminal to alert the user that he had incorrectly typed in his expression.
Another case that is overlooked is when the user types in a division operation with zero as the divisor. You know by now that you should never attempt to divide a number by zero in Objective-C. The program should check for this case.
Trying to predict the ways in which a program can fail or produce unwanted results and then taking preventive measures to account for such situations is a necessary part of producing good, reliable programs. Running a sufficient number of test cases against a program can often point a finger to portions of the program that do not account for certain cases. But it goes further than that. It must become a matter of self-discipline while coding a program to always ask, “What would happen if…?” and to insert the necessary program statements to handle the situation properly.
Program 6.8A, a modified version of Program 6.8, accounts for division by zero and the keying in of an unknown operator.
// Program to evaluate simple expressions of the form
// value operator value
#import <stdio.h>
#import <objc/Object.h>
// Insert interface and implementation sections for
// Calculator class here
int main (int argc, char *argv[])
{
double value1, value2;
char operator;
Calculator *deskCalc = [[Calculator alloc] init];
printf ("Type in your expression.
");
scanf ("%lf %c %lf", &value1, &operator, &value2);
[deskCalc setAccumulator: value1];
if ( operator == '+' )
[deskCalc add: value2];
else if ( operator == '-' )
[deskCalc subtract: value2];
else if ( operator == '*' )
[deskCalc multiply: value2];
else if ( operator == '/' )
if ( value2 == 0 )
printf ("Division by zero.
");
else
[deskCalc divide: value2];
else
printf ("Unknown operator.
");
printf ("%.2f
", [deskCalc accumulator]);
[deskCalc free];
return 0;
}
Type in your expression.
123.5 + 59.3
182.80
Type in your expression.
198.7 / 0
Division by zero.
198.7
Type in your expression.
125 $ 28
Unknown operator.
125
When the operator that is typed in is the slash, for division, another test is made to determine whether value2
is 0. If it is, an appropriate message is displayed at the terminal; otherwise, the division operation is carried out and the results are displayed. Pay careful attention to the nesting of the if
statements and the associated else
clauses in this case.
The else
clause at the end of the program catches any fall throughs. Therefore, any value of operator
that does not match any of the four characters tested causes this else
clause to be executed, resulting in the display of Unknown operator.
at the terminal.
A better way to handle the division by zero problem is to perform the test inside the method that handles division. So, you can modify your divide:
method as shown here:
-(void) divide: (double) value
{
if (value != 0.0)
accumulator /= value;
else {
printf ("Division by zero.
");
accumulator = 99999999.;
}
}
If value
is nonzero, you perform the division; otherwise, you display the message and set the accumulator to 99999999. This is arbitrary; you could have set it to zero or perhaps set a special variable to indicate an error condition. In general, it's better to have the method handle special cases rather than rely on the resourcefulness of the programmer using the method.
switch
StatementThe type of if
-else
statement chain you encountered in the last program example—where the value of a variable is successively compared against different values—is so commonly used when developing programs that a special program statement exists in the Objective-C language for performing precisely this function. The name of the statement is the switch
statement, and its general format is as follows:
switch ( expression )
{
case value1:
program statement
program statement
...
break;
case value2:
program statement
program statement
...
break;
...
case valuen:
program statement
program statement
...
break;
default:
program statement
program statement
...
break;
}
The expression
enclosed within parentheses is successively compared against the values value1
, value2
, …, valuen
, which must be simple constants or constant expressions. If a case is found whose value is equal to the value of expression
, the program statements that follow the case are executed. You will note that when more than one such program statement is included, they do not have to be enclosed within braces.
The break
statement signals the end of a particular case and causes execution of the switch
statement to be terminated. Remember to include the break
statement at the end of every case. Forgetting to do so for a particular case causes program execution to continue into the next case whenever that case is executed. Sometimes this is done intentionally; if you elect to do so, be sure to insert comments to alert others of your purpose.
The special optional case called default
is executed if the value of expression
does not match any of the case values. This is conceptually equivalent to the catchall else
used in the previous example. In fact, the general form of the switch
statement can be equivalently expressed as an if
statement as follows:
if ( expression == value1 )
{
program statement
program statement
...
}
else if ( expression == value2 )
{
program statement
program statement
...
}
...
else if ( expression == valuen )
{
program statement
program statement
...
}
else
{
program statement
program statement
...
}
Bearing the previous code in mind, you can translate the big if
statement from Program 6.8A into an equivalent switch
statement. We will call this new program Program 6.9.
// Program to evaluate simple expressions of the form
// value operator value
#import <stdio.h>
#import <objc/Object.h>
// Insert interface and implementation sections for
// Calculator class here
int main (int argc, char *argv[])
{
double value1, value2;
char operator;
Calculator *deskCalc = [[Calculator alloc] init];
printf ("Type in your expression.
");
scanf ("%lf %c %lf", &value1, &operator, &value2);
[deskCalc setAccumulator: value1];
switch ( operator ) {
case '+':
[deskCalc add: value2];
break;
case '-':
[deskCalc subtract: value2];
break;
case '*':
[deskCalc multiply: value2];
break;
case '/':
[deskCalc divide: value2];
break;
default:
printf ("Unknown operator.
");
break;
}
printf ("%.2f
", [deskCalc accumulator]);
[deskCalc free];
return 0;
}
Type in your expression.
178.99 - 326.8
-147.81
After the expression has been read in, the value of operator
is successively compared against the values as specified by each case. When a match is found, the statements contained inside the case are executed. The break
statement then sends execution out of the switch
statement, where execution of the program is completed. If none of the cases matches the value of operator, the default
case, which displays Unknown operator.
, is executed.
The break
statement in the default
case is actually unnecessary in the preceding program because no statements follow this case inside the switch
. Nevertheless, it is a good programming habit to remember to include the break
at the end of every case.
When writing a switch
statement, you should bear in mind that no two case values can be the same. However, you can associate more than one case value with a particular set of program statements. This is done simply by listing the multiple case values (with the keyword case
before the value and a colon after the value in each case) before the common statements that are to be executed. As an example, in the switch
statement
switch ( operator )
{
...
case '*':
case 'x':
[deskCalc multiply: value2];
break;
...
}
the multiply:
method is executed if operator
is equal to an asterisk or to the lowercase letter x.
Just about anyone learning to program soon finds herself with the task of having to write a program to generate a table of prime numbers. To refresh your memory, a positive integer, p, is a prime number if it is not evenly divisible by any other integers, other than 1 and itself. The first prime integer is defined to be 2. The next prime is 3 because it is not evenly divisible by any integers other than 1 and 3; and 4 is not prime because it is evenly divisible by 2.
You could take several approaches to generate a table of prime numbers. If you had the task of generating all prime numbers up to 50, for example, then the most straightforward (and simplest) algorithm to generate such a table would simply test each integer, p, for divisibility by all integers from 2 through p-1. If any such integer evenly divided p, then p would not be prime; otherwise, it would be a prime number.
// Program to generate a table of prime numbers
#import <stdio.h>
int main (int argc, char *argv[])
{
int p, d, isPrime;
for ( p = 2; p <= 50; ++p ) {
isPrime = 1;
for ( d = 2; d < p; ++d )
if ( p % d == 0 )
isPrime = 0;
if ( isPrime != 0 )
printf ("%i ", p);
}
printf ("
");
return 0;
}
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47
Several points are worth noting about Program 6.10. The outermost for
statement sets up a loop to cycle through the integers 2–50. The loop variable p
represents the value you are currently testing to see whether it is prime. The first statement in the loop assigns the value 1
to the variable isPrime
. The use of this variable will become apparent shortly.
A second loop is set up to divide p
by the integers 2–p
-1. Inside the loop, a test is performed to see whether the remainder of p
divided by d
is 0. If it is, you know that p
cannot be prime because an integer other than 1 and itself evenly divides it. To signal that p
is no longer a candidate as a prime number, the value of the variable isPrime
is set equal to 0
.
When the innermost loop finishes execution, the value of isPrime
is tested. If its value is not equal to zero, no integer was found that evenly divided p
; therefore, p
must be a prime number, and its value is displayed.
You might have noticed that the variable isPrime
takes on either the value 0
or 1
, and no other values. Its value is 1
as long as p
still qualifies as a prime number. But as soon as a single even divisor is found, its value is set to 0
to indicate that p
no longer satisfies the criteria for being prime. Variables used in such a manner are generally referred to as Boolean variables. A flag typically assumes only one of two different values. Furthermore, the value of a flag usually is tested at least once in the program to see whether it is on (TRUE
or YES
) or off (FALSE
or NO
) and some particular action is taken based on the results of the test.
In Objective-C, the notion of a flag being TRUE
or FALSE
is most naturally translated into the values 1
and 0
, respectively. So, in Program 6.10, when you set the value of isPrime
to 1
inside the loop, you are effectively setting it TRUE
to indicate that p
“is prime.” If, during the course of execution of the inner for
loop, an even divisor is found, the value of isPrime
is set FALSE
to indicate that p
no longer “is prime.”
It is no coincidence that the value 1
is typically used to represent the TRUE
or on state and 0
is used to represent the FALSE
or off state. This representation corresponds to the notion of a single bit inside a computer. When the bit is on, its value is 1
; when it is off, its value is 0
. But in Objective-C, there is an even more convincing argument in favor of these logic values. It has to do with the way the Objective-C language treats the concept of TRUE
and FALSE
.
When we began our discussions in this chapter, we noted that if the conditions specified inside the if
statement were satisfied, the program statement that immediately followed would be executed. But what exactly does satisfied mean? In the Objective-C language, satisfied means nonzero, and nothing more. Thus, the statement
if ( 100 )
printf ("This will always be printed.
");
results in the execution of the printf
statement because the condition in the if
statement (in this case simply the value 100
) is nonzero and therefore is satisfied.
In each of the programs in this chapter, the notions of “nonzero means satisfied” and “zero means not satisfied” were used. This is because, whenever a relational expression is evaluated in Objective-C, it is given the value 1
if the expression is satisfied and 0
if the expression is not satisfied. So, evaluation of the statement
if ( number < 0 )
number = -number;
actually proceeds as follows: The relational expression number < 0
is evaluated. If the condition is satisfied, that is, if number
is less than 0, the value of the expression is 1
; otherwise, its value is 0
.
The if
statement tests the result of the expression evaluation. If the result is nonzero, the statement that immediately follows is executed; otherwise, the statement is skipped.
The preceding discussion also applies to the evaluation of conditions inside the for
, while
, and do
statements. Evaluation of compound relational expressions such as in the statement
while ( char != 'e' && count != 80 )
also proceeds as outlined previously. If both specified conditions are valid, the result is 1
; but if either condition is not valid, the result of the evaluation is 0
. The results of the evaluation are then checked. If the result is 0
, the while
loop terminates; otherwise it continues.
Returning to Program 6.10 and the notion of flags, it is perfectly valid in Objective-C, to test whether the value of a flag is TRUE
by an expression such as
if ( isPrime )
which is equivalent to
if ( isPrime != 0 )
To easily test whether the value of a flag is FALSE
, you use the logical negation operator, !
. In the expression
if ( ! isPrime )
the logical negation operator is used to test whether the value of isPrime
is FALSE
(read this statement as “if not isPrime
”). In general, an expression such as
! expression
negates the logical value of expression
. So, if expression
is 0
, the logical negation operator produces a 1
. And if the result of the evaluation of expression
is nonzero, the negation operator yields a 0
.
The logical negation operator can be used to easily flip the value of a flag, such as in the following expression:
my_move = ! my_move;
As you might expect, this operator has the same precedence as the unary minus operator, which means that it has higher precedence than all binary arithmetic operators and all relational operators. So, to test whether the value of a variable x
is not less than the value of a variable y
, such as in
! ( x < y )
the parentheses are required to ensure proper evaluation of the expression. Of course, you could have equivalently expressed the previous statement as
x >= y
A couple of built-in features in Objective-C make working with Boolean variables a little easier. One is the special type BOOL
, which can be used to declare variables that will contain either a true or false value.2 The other is the built-in values YES
and NO
. Using these predefined values in your programs can make them easier to write and read. Here is Program 6.10 rewritten to take advantage of these features.
// Program to generate a table of prime numbers
// second version using BOOL type and predefined values
#import <stdio.h>
#import <objc/Object.h>
int main (int argc, char *argv[])
{
int p, d;
BOOL isPrime;
for ( p = 2; p <= 50; ++p ) {
isPrime = YES;
for ( d = 2; d < p; ++d )
if ( p % d == 0 )
isPrime = NO;
if ( isPrime == YES )
printf ("%i ", p);
}
printf ("
");
return 0;
}
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47
Even though you haven't defined any classes in Program 6.10A, you need to import objc/Object.h
because BOOL, YES
, and NO
are defined there.
Perhaps the most unusual operator in the Objective-C language is one called the conditional operator. Unlike all other operators in Objective-C—which are either unary or binary operators—the conditional operator is a ternary operator; that is, it takes three operands. The two symbols used to denote this operator are the question mark (?
) and the colon (:
). The first operand is placed before the ?
, the second between the ?
and the :
, and the third after the :
.
The general format of the conditional expression is
condition ? expression1 : expression2
In this syntax, condition
is an expression, usually a relational expression, that is evaluated by the Objective-C system first whenever the conditional operator is encountered. If the result of the evaluation of condition
is TRUE
(that is, nonzero), expression1
is evaluated and the result of the evaluation becomes the result of the operation. If condition
evaluates FALSE
(that is, zero), expression2
is evaluated and its result becomes the result of the operation.
A conditional expression is most often used to assign one of two values to a variable depending on some condition. For example, suppose you have an integer variable x
and another integer variable s
. If you wanted to assign -1
to s
if x
were less than 0
, and the value of x
2 to s
otherwise, the following statement could be written:
s = ( x < 0 ) ? -1 : x * x;
The condition x < 0
is first tested when the previous statement is executed. Parentheses are generally placed around the condition expression to aid in the statement's readability. This is usually not required, though, because the precedence of the conditional operator is very low—lower, in fact, than all other operators but the assignment operators and the comma operator.
If the value of x
is less than zero, the expression immediately following the ?
is evaluated. This expression is simply the constant integer value -1
, which is assigned to the variable s
if x
is less than zero.
If the value of x
is not less than zero, the expression immediately following the :
is evaluated and assigned to s
. So, if x
is greater than or equal to zero, the value of x * x
, or x
2, is assigned to s
.
As another example of the use of the conditional operator, the following statement assigns to the variable max_value
the maximum of a
and b
:
max_value = ( a > b ) ? a : b;
If the expression that is used after the :
(the “else” part) consists of another conditional operator, you can achieve the effects of an “else if” clause. For example, the sign function implemented in Program 6.6 can be written in one program line using two conditional operators, as follows:
sign = ( number < 0 ) ? -1 : (( number == 0 ) ? 0 : 1);
If number
is less than zero, sign
is assigned the value -1
; else, if number
is equal to zero, sign
is assigned the value 0
; else, it is assigned the value 1
. The parentheses around the “else” part of the previous expression are actually unnecessary. This is because the conditional operator associates from right to left, meaning that multiple uses of this operator in a single expression, such as in
e1 ? e2 : e3 ? e4 : e5
group from right to left and therefore are evaluated as follows:
e1 ? e2 : ( e3 ? e4 : e5 )
Conditional expressions don't have to be used on the right side of an assignment—they can be used in any situation in which expressions could be used. This means you could display the sign of the variable number
without first assigning it to a variable using a printf
statement as shown here:
printf ("Sign = %i
", ( number < 0 ) ? -1
: ( number == 0 ) ? 0 : 1);
The conditional operator is very handy when writing preprocessor macros in Objective-C. This can be seen in detail in Chapter 12, “The Preprocessor.”
print
method from the Fraction
class so that whole numbers are displayed as such (so the fraction 5/1 should display as simply 5). Also modify the method to display fractions with a numerator of 0 as simply zero.number operator
The following operators should be recognized by the program:
+ - * / S E
The S
operator tells the program to set the accumulator to the typed-in number, and the E
operator tells the program that execution is to end. The arithmetic operations are performed on the contents of the accumulator with the number that was keyed in acting as the second operand. The following is a sample run showing how the program should operate:
Begin Calculations
10 S Set Accumulator to 10
= 10.000000 Contents of Accumulator
2 / Divide by 2
= 5.000000 Contents of Accumulator
55 - Subtract 55
-50.000000
100.25 S Set Accumulator to 100.25
= 100.250000
4 * Multiply by 4
= 401.000000
0 E End of program
= 401.000000
End of Calculations.
Make sure that the program detects division by 0 and also checks for unknown operators. Use the Calculator
class developed in Program 6.8 for performing your calculations.
nine three two
(Remember to display zero
if the user types in just a 0.) Note: This exercise is a hard one!
for
loop is also inefficient because the value of p
is always divided by all values of d
from 2 through p
–1. This inefficiency could be avoided if you added a test for the value of isPrime
in the conditions of the for
loop. In this manner, the for
loop could be set up to continue as long as no divisor was found and the value of d
was less than p
. Modify Program 6.10 to incorporate these two changes; then run the program to verify its operation. Note: In a later chapter, you will learn even more efficient ways of generating prime numbers.18.226.251.217