Logical Operators

   


So far, our Boolean expressions have consisted of only simple conditions, such as balance <= 20000 in line 13 of Listing 8.4 or (choice == "A") in line 11 of Listing 8.5, with only one comparison operator. However, it is often beneficial to combine several simple conditions (sub-Boolean expressions) into one larger Boolean expression. Logical operators allow us to do exactly that.

C# contains three logical operators that are semantically equivalent to "and," "or," and "not" in our everyday language. The latter are used in Figure 8.12 as a general introduction to C#'s equivalents. Notice that each phrase could be part of a normal conversation, and that we intuitively understand their meaning.

Figure 8.12. Three logical "operators" of the English language and, or, and not.
graphics/08fig12.gif

The equivalents to and, or, and not from spoken English, are in the world of logic called logical AND, logical OR, and logical NOT. Logical AND is in C# symbolized by && (two adjacent ampersands); logical OR is symbolized by || (two vertical lines); logical NOT is written as ! (exclamation point).

The science of logic investigates the rules governing reliable deductions.

Note

graphics/common.gif

It is important to realize that the meaning associated with && (AND), || (OR), and ! (NOT) when used in C# does not deviate significantly from the meaning of and, or, and not in everyday speech. C# has merely formalized the meaning to iron out any ambiguities found in spoken language.


A logical operator (&&, ||, !) can only be applied to expressions of type bool and will always return a value of type bool. Another common term for logical operator is Boolean operator.

Table 8.2 provides a brief overview of the three operators. Notice that AND, OR, and NOT are sometimes referred to as conjunction, disjunction, and negation.

Table 8.2. Three Commonly Used Logical Operators
Logical operator Other term used C# Symbol Number of operands Example
AND Conjunction && 2 (binary) (5<3) && (10<20)
OR Disjunction || 2 (binary) (mass < 800) || (distance >1000)
NOT Negation ! 1 (unary) !(mass > 8000)

Note

graphics/common.gif

C# contains three additional logical operators apart from those mentioned in Table 8.2. They are called logical bitwise operators and are symbolized by single ampersand (&), single vertical line (|) arrow pointing up (^). The two former operators & and | have the same logical meaning as their close relatives && and ||, respectively, but with subtle differences during their evaluation. The last logical operator ^ is called the exclusive operator.

Because the logical bitwise operators are used less frequently than their cousins in Table 8.2. They will not be part of the main discussion here, but are presented at the end of this section.


The Logical AND Operator: &&

The logical AND operator (&&) is a binary operator and it combines two sub-Boolean expressions to form one larger Boolean expression (see Syntax Box 8.6).

Syntax Box 8.6: Forming Larger Boolean Expressions with the && Operator

 Larger_Boolean_expression::=                       <Sub_Boolean_expression1> && <Sub_Boolean_expression2> graphics/ccc.gif 

By combining the two possible values (true and false) of <Sub_Boolean_expression1> with the two possible values of <Sub_Boolean_expression2>, we can form a truth table (see Table 8.3) with the four (2x2) different possible combinations and their resulting values when combined by the AND operator (&&).

A truth table provides a complete list of all the possible combinations of values of the variables in a Boolean expression and the resulting value for each combination. Tables 8.3, 8.4, 8.5, 8.6, 8.7, 8.8 and 8.9 are all examples of truth tables.

Table 8.3. Truth Table for the Logical AND (&&) Operator
Value of sub_expression1 Value of sub_expression2 Value of larger Boolean expression: sub_expression1 && sub_expression2 Example
false false false (5>10) && (10<3)
false true false (5==2) && (10>2)
true false false (5!=0) && (3!=3)
true true true (7>4) && (9<10)

Notice that only when both the original expressions of Table 8.3 are true does the resulting expression represent the value true; otherwise, the value is false.

Note

graphics/common.gif

According to Appendix B (located at this book's Web site at www.samspublishing.com), all the comparison operators have higher precedence than the && and || operators, so the parentheses applied in the examples of Table 8.3 are redundant. (5>10) && (10<3) is equivalent to 5>10 && 10<3. However, the intent of the former is clearer.


Recall the balance assessment program of Listing 8.4 presented earlier. As promised, it is possible to significantly simplify this program with the use of logical operators; let's see how.

The basic strategy is to reduce the number of required nested if statements by letting the Boolean expression of each if statement more precisely express the logic we are implementing. Recall our three categories from the Big Bucks Bank:

  1. 5000 balance 20000

  2. 60000 balance 75000

  3. Neither A nor B

The Boolean expression of each if statement in Listing 8.4 was only partly able to describe (express) each category and had to be combined with Boolean expressions of other nested if statements to complete the picture. If we can somehow express the mathematical notation 5000 balance 20000 in one Boolean expression, we can write the logic shown in Figure 8.13 and reduce the number of nested if statements from four to two.

Figure 8.13. A simpler but invalid implementation of the logic in Listing 8.4.
graphics/08fig13.gif

The code in Figure 8.13 has certainly simplified the multibranch if-else statement, but it has one serious drawback the Boolean expressions are invalid. It is not possible to use the mathematical notation 5000 balance 20000 in the C# source code as in the following:

 if (5000 <= balance <= 20000)    //Invalid 

Any such comparisons must be broken down into sub-expressions with only one comparison operator and then connected fittingly with logical operators. Fortunately, it is possible by following this valid system to state the meaning contained in any logic expression.

But how do we express 5000 <= balance <= 20000 in this system, and how can we show that our new expression is correct? Well, many simpler expressions can be formed out of intuition and common sense, whereas more complicated expressions require knowledge from the science of logic. In our case, we try with common sense and convince ourselves that the meaning of

 5000 <= balance <= 20000 

had it been valid, is equivalent to

 (5000 <= balance) && (balance <= 20000) 

To prove that our common sense is correct, and that the two expressions in fact are identical, we can compare their truth tables. The truth table for 5000 <= balance <= 20000 is shown in Table 8.4. Notice that each of the first two columns must be filled out in the standard fashion introduced in Table 8.3, so they contain exactly the same entries.

Table 8.4. Truth Table for the Proposition 5000 <= balance <= 20000
5000 balance balance 20000 5000 balance 20000
false false false
false true false
true false false
true true true

The truth table for && is simply constructed by copying the values given in &&'s truth table of Table 8.3 and inserting balance >= 5000 and balance <= 20000 in the place of the sub-expressions. This results in Table 8.5.

Table 8.5. Truth Table for the Proposition 5000 balance AND balance 20000
balance >= 5000 balance <= 20000 5000 <= balance && balance <= 20000
false false false
false true false
true false false
true true true

Because the entries of the two tables are identical, we conclude that our common sense indeed was correct, in that

 5000  balance  20000 

is equivalent to

 5000 <= balance && balance <=  20000 

and, consequently, we can simplify the nested if statements of our Big Bucks Bank application in a similar fashion to that shown in Figure 8.13. Lines 11 16 of Listing 8.7 present the resulting multibranch if-else statements.

Listing 8.7 CompactBalanceAssessment.cs
01: using System; 02: 03: class CompactBalanceAssessment 04: { 05:     public static void Main() 06:     { 07:         decimal balance; 08: 09:         Console.Write("Enter balance: "); 10:         balance = Convert.ToDecimal(Console.ReadLine()); 11:         if(balance >= 5000 && balance <= 20000) 12:             Console.WriteLine("Balance is in category A"); 13:         else if(balance >= 60000 && balance <= 75000) 14:             Console.WriteLine("Balance is in category B"); 15:         else 16:             Console.WriteLine("Balance is in category C"); 17:     } 18: } 

The output is the same as that from Listing 8.4.

By utilizing the && operator in lines 11 and 13, we reduce the number of nested if-else statements from four to two. This is possible since we have shown that:

balance >= 5000 && balance <= 20000 is logically equivalent to 5000 >= balance >= 20000

and that:

balance >= 5000 && balance <= 20000 is logically equivalent to 5000 >= balance >= 20000

The Logical OR Operator: ||

The logical operator OR, written with two vertical lines ||, is a binary operator and, as such, combines two operands, as shown in Syntax Box 8.7.

Syntax Box 8.7 Forming Larger Boolean Expressions with the || Operator

 Larger_Boolean_expression::=                 <Sub_Boolean_expression1> ||  graphics/ccc.gif<Sub_Boolean_expression2> 

The truth table of the logical OR operator shown in Table 8.6 tells us that only if both sub-expressions are false (first row) will the larger expression be false; otherwise, it will be true. This nicely fits in with our usual connotation of the word "or." For example in the following sentence


graphics/08infig05.gif

both conditions need to be false for today not to be a weekend.

Table 8.6. Truth Table for the Logical OR (||) Operator
Value of sub-expression1 Value of sub-expression2 sub-expression1 || sub-expression2
false false false
false true true
true false true
true true true

Example:

Sometimes theatres and cinemas offer discounted tickets to people who fit into a certain age category. In this case, a person must be either less than 15 or more than 65 years old to get a discounted ticket. Listing 8.8 utilizes the || operator along with this rule to determine whether a person is entitled to a discount.

Listing 8.8 DiscountOrNot.cs
01: using System; 02: 03: class DiscountOrNot 04: { 05:     public static void Main() 06:     { 07:         int age; 08: 09:         Console.Write("Enter your age: "); 10:         age = Convert.ToInt32(Console.ReadLine()); 11:         if ((age < 15) || (age > 65)) 12:             Console.WriteLine("Congratulations! You get a discount"); 13:         else 14:             Console.WriteLine("Sorry no discount for you"); 15:     } 16: } Enter your age: 10<enter> Congratulations! You get a discount 

Line 11 utilizes the || operator to determine whether age is less than 15 or greater than 65. If this is the case, line 12 is executed; otherwise, the flow of control is passed to line 14.

You might use as many logical operators in one Boolean expression as you want, but must then keep in mind that the && operator has a higher precedence than the || operator. Consequently, you might need to apply parentheses to get a correct result.

Example:

A programmer wants to implement a simple program that sifts through a list of cars for sale in a database to locate cars with suitable characteristics. In this case, the programmer is interested in the price, age, and maxSpeed. The price is the main concern, so as long as the price is less than $5,000.00, she is happy if just one of the following two criteria is true:

  • age < 10 years

  • maxSpeed > 120 mph.

The following is a first attempt to implement these rules:

 if (price < 5000 && age < 10 || maxSpeed > 120)  //Incorrect first attempt 

However, due to the higher precedence of the && operator, the Boolean expression is evaluated as

 if ((price < 5000 && age < 10) || maxSpeed > 120) 

which represents a different logic. It locates all cars with a maxSpeed greater than 120, disregarding the price and age. All other cars will only be selected if both (price < 5000) and (age < 10) are true. To rectify the problem, we need to apply a couple of parentheses, forcing the || operator to be applied first:

 if (price < 5000 && (age < 10 || maxSpeed > 120))  //Correct 

Let's compare the truth tables (see Table 8.7) of the invalid and the valid car evaluation expressions to pinpoint the differences between them. Notice that the differences between the two expressions appear in the second and fourth rows, the rest are identical.

Table 8.7. Comparing Truth Tables of the Two Car Evaluation Expressions
Price < 5000 Age < 10 MaxSpeed > 120 (price < 5000 && age < 10 || maxSpeed > 120) (price < 5000 && (age < 10 || maxSpeed > 120))
false false false false false
false false true true false
false true false false false
false true true true false
true false false false false
true false true true true
true true false true true
true true true true true

Note

graphics/common.gif

To write down all the combinations of true and false for the sub-expression in a truth table, use the system applied in Table 8.7. The leftmost column has false written in the four top rows and true in the four bottom rows. The next column has two falses and two trues, and every second row is false in the thirdmost column.


Short Circuit Evaluation and the Bitwise Operators & and |

When the program evaluates a Boolean expression containing the && operator, such as

 (distance == 1000) && (mass ==3000) 

it will first evaluate (distance == 1000). If this part turns out to be false, the compiler knows that according to the && truth table, the whole expression must be false, irrespective of the value the second part ((mass == 3000)). To save time, the compiler skips the evaluation of (mass == 3000). This mechanism is referred to as short-circuiting.

Similarly, if the program contains the following || expression

 (distance == 1000) || (mass == 3000) 

and the first part ((distance == 1000)) is evaluated to be true, the latter part will be ignored, because its value, according to the OR truth table, will not make any difference to the value of the full expression.

C# contains two additional logical operators called bitwise AND symbolized with a single ampersand (&) and bitwise OR symbolized by a single vertical line (|). The truth tables for & and | are identical to those of && and ||, respectively. However, & and | do not cause short circuit evaluation and will, therefore, perform a fraction slower than their short-circuiting equivalents.

In most cases, && and || will be the preferred operators to use, but in rare instances, you might want the program to complete the evaluation of a Boolean expression, even if it is not needed to determine the value of the overall Boolean expression.

For example, recall the increment (++) and decrement (--) arithmetic operators from Chapter 7. Just as they can be part of a longer arithmetic expression, they can also be part of a Boolean expression, such as the following:

 if ((++distance == 1000) && (++count <= 10)) 

whenever (++count <= 10) is evaluated, count is first incremented by one and then compared with 10. However, due to the short-circuiting nature of &&, (++count <= 10) is only evaluated if (++distance == 1000) is true. As a consequence, count will only be incremented by one when distance is equal to 1000, which was probably not what the programmer had in mind. To make sure that the increment always takes place when this line is executed, the programmer can make use of the bitwise operator &:

 if ((++distance == 1000) & (++count <= 10)) 

Tip

graphics/bulb.gif

Even though source code with increment and decrement operators applied inside longer expression might look smart and sophisticated at first glance, they usually represent bug-prone code, difficult to read and, hence, not worthwhile. Thus, instead of writing

 if ((++distance == 1000) & (++count <= 10)) 

break the line up into three lines:

 ++distance; ++count; if ((distance == 1000) && (count <= 10)) 

There is no doubt as to how these three lines are processed.


Sometimes, programmers use a certain style for constructing a Boolean expression that requires short-circuit evaluation to avoid runtime errors. This is exemplified in the following line:

 if ((speed != 0)  && ((distance / speed) > 100)) 

In general, division by zero produces an infinitely large value and a runtime exception. Thus, if speed in the expression is equal to zero, the second part of the expression (((distance / speed) > 100)) generates a runtime exception. However, because (speed != 0), along with the short circuiting of the && operator, only allows the second part to be executed if speed is different from zero, the line will never generate a runtime exception due to this problem. This would obviously not be the case had we applied the bitwise & operator instead.

The Bitwise Exclusive OR Operator: ^

The || operator is sometimes referred to as inclusive OR and is, in many cases, equivalent to the meaning of "or" in our spoken language. For example, if I ask you

Would you like salt or pepper?

"or" has the same meaning as ||. It is implicitly understood that you can choose just the salt or just the pepper or even both; all three cases would lead to true, similar to our truth table for || (see Table 8.6). Only if you decide against both offers does the proposition become false. But what about the next question:

Would you like to go to the cinema or the theatre tonight?

where the intent is to either go to the cinema or the theatre but not both. Thus, if you choose neither or if you choose both, the proposition is false. Only if you choose just the cinema or just the theatre will it be true. This meaning of "or" is referred to as exclusive OR and is symbolized by ^ in C#. The truth table is shown in Table 8.8.

Table 8.8. Truth Table for the Logical Exclusive OR Operator: ^
Value of sub-expression1 Value of sub-expression2 sub-expression1 ^ sub-expression2
false false false
false true true
true false true
true true false

Note

graphics/common.gif

The ambiguous connotation of "or" in our spoken language can sometimes have important implications.

Consider this scenario: Your house is damaged by fire and, during the fire, a thief takes all your belongings. After this ordeal, you rush to check your insurance policy and are relieved when you see the following line:

"…and you are insured against theft or fire…"

Then it suddenly strikes you (because you've just been reading your favorite chapter about logical operators in some book about C#) that the insurance company might interpret "or" in the contract as exclusive "or." You're freaked out by the thought of the implications: Because the two misfortunes happened at the same time, you get zilch compensation. Only if the house is on fire exclusively, or if the house is broken into exclusively will you get a payment.


The Logical NOT Operator: !

The logical NOT (!)operator is, as opposed to the binary operators && and ||, a unary operator, as shown in Syntax Box 8.8. Just as the unary minus operator - reverses the sign of a numerical expression, the logical NOT operator inverts the truth value of the Boolean expression to which it is applied. In other words, if the value of myBooleanExpression is true, !myBooleanExpression is false, and vice versa. This is displayed in Table 8.9.

Syntax Box 8.8 Forming Boolean Expressions with the ! Operator

 Boolean_expression::= !<Sub_Boolean_expression> 

Note

graphics/common.gif

A popular name for the ! operator is bang. Thus, !isReady is pronounced "bang is Ready."


Note

graphics/common.gif

The ! operator does not change the value of the expression to which it is applied. It merely changes the value generated by the expression.


Table 8.9. Truth Table for the Logical NOT (!) Operator
Value of sub-expression !sub-expression
false true
true false

In many cases, Boolean expressions are clearer if they don't involve negations. Fortunately, it is often possible to steer clear of the negation operator by rearranging the operands and by carefully selecting the comparison operators applied in a Boolean expression. Table 8.10 provides a few examples.

Table 8.10. Negated Boolean Expressions and Their Non-Negated Equivalent Counterparts
Boolean expression with ! operator Clearer equivalent Boolean expression without the ! operator
!(x == 10) (x != 10)
!(x > 10) (x <= 10)
!(x <= 10) (x > 10)

Note

graphics/common.gif

As opposed to && and || , the ! operator has a higher precedence than any of the arithmetic or comparison operators. Thus, you should always enclose a Boolean expression involving those operators in parentheses before negating it. For example

 ! (10 > 20)  //Valid ! 10 > 20    //Invalid 

The first expression is valid because the parentheses enclose a Boolean expression.

However, the second expression is interpreted as (! 10) > 20, applying the ! operator to 10, which is an int and a non-Boolean expression. As a result, a compiler error is triggered in this case.


If you are constructing an if-else statement and are trying to remove an ! operator stalking the associated Boolean expression, the following method might be helpful.

Consider the following two code snippets.


graphics/08infig06.gif

The only effect of the ! operator in this if-else statement is to redirect the flow from the true if part to the false else part and vice versa. Thus, by removing the ! operator as well as swapping the contents of the if part and the else part, the logic of the if-else statements remains unchanged.

Until now, we have merely tried to avoid our poor and lonely ! operator. Isn't it useful for anything? Yes, it is useful in cases where it is impossible to exchange one comparison operator with another or swapping statements in if-else statements as demonstrated above. We bump into those cases when working with methods returning a value of type bool. Recall our TextAnalyzer.cs program from Listing 7.8, in Chapter 7. It utilized a whole host of methods found in the System.Char structure, one of which was the IsWhiteSpace() method. It returned the value true if the character provided as an argument was a whitespace character; otherwise, it returned false. But what if we wanted to count the non-whitespace characters of a text? Because there is no IsNotWhiteSpace() method or something similar, we can utilize the ! operator instead to simulate such a method. This is done in line 17 of Listing 8.9, which counts all non-whitespace characters of a text.

Listing 8.9 CharacterCounter.cs
01: using System; 02: 03: class CharacterCounter 04: { 05:     public static void Main() 06:     { 07:         string myText; 08:         int charCount = 0; 09:         char ch; 10:         int index = 0; 11: 12:         Console.WriteLine("Enter some text"); 13:         myText = Console.ReadLine(); 14:         while (index < myText.Length) 15:         { 16:             ch = myText[index]; 17:             if (!char.IsWhiteSpace(ch)) 18:                 charCount++; 19:             index++; 20:         } 21:         Console.WriteLine("Number of non-white-space characters: {0:N0} ", charCount); 22:     } 23: } 

Sample 1:

 Enter some text Beethoven<enter> Number of non-white-space characters: 9 

Sample 2:

 Enter some text B e e    th  o   v     e   n<enter> Number of non-white-space characters: 9 

As long as (index < myText.Length) in line 14 is true, the compound statement of the while statement spanning from line 15 to line 20 is repeated. Because index is initialized to 0 in line 10, and every repetition of the compound statement increments index by 1 in line 19, the loop is repeated myText.Length times, allowing the algorithm to analyze every position of myText in lines 17 and 18. We are able to detect all non-whitespace characters by applying the ! operator in line 17. Every time a non-whitespace character is encountered, charCount is incremented by one.


   


C# Primer Plus
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2000
Pages: 286
Authors: Stephen Prata

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net