# 7. Selection ## 7.1. Boolean Values and Boolean Expressions The Python type for storing true and false values is called `bool`, named after the British mathematician, George Boole. George Boole created *Boolean Algebra*, which is the basis of all modern computer arithmetic. There are only two **boolean values**. They are `True` and `False`. Capitalization is important, since `true` and `false` are not boolean values (remember Python is case sensitive). ```python print(True) print(type(True)) print(type(False)) ``` Note Boolean values are not strings! It is extremely important to realize that True and False are not strings. They are not surrounded by quotes. They are the only two values in the data type `bool`. Take a close look at the types shown below. ```python print(type(True)) print(type("True")) ``` A **boolean expression** is an expression that evaluates to a boolean value. The equality operator, `==`, compares two values and produces a boolean value related to whether the two values are equal to one another. ```python print(5 == 5) print(5 == 6) j = "hel" print(j + "lo" == "hello") ``` In the first statement, the two operands are equal, so the expression evaluates to `True`. In the second statement, 5 is not equal to 6, so we get `False`. The `==` operator is one of six common **comparison operators**; the others are: ```python x != y # x is not equal to y x > y # x is greater than y x < y # x is less than y x >= y # x is greater than or equal to y x <= y # x is less than or equal to y ``` Although these operations are probably familiar to you, the Python symbols are different from the mathematical symbols. A common error is to use a single equal sign (`=`) instead of a double equal sign (`==`). Remember that `=` is an assignment operator and `==` is a comparison operator. Also, there is no such thing as `=<` or `=>`. Note too that an equality test is symmetric, but assignment is not. For example, if `a == 7` then `7 == a`. But in Python, the statement `a = 7` is legal and `7 = a` is not. (Can you explain why?) **Check your understanding** Which of the following is a Boolean expression? Select all that apply. * True * True and False are both Boolean literals. * 3 == 4 * The comparison between two numbers via == results in either True or False (in this case False), both Boolean values. * 3 + 4 * 3 + 4 evaluates to 7, which is a number, not a Boolean value. * 3 + 4 == 7 * 3 + 4 evaluates to 7. 7 == 7 then evaluates to True, which is a Boolean value. * "False" * With the double quotes surrounding it, False is interpreted as a string, not a Boolean value. If the quotes had not been included, False alone is in fact a Boolean value. ## 7.2. Logical operators There are three **logical operators**: `and`, `or`, and `not`. The semantics (meaning) of these operators is similar to their meaning in English. For example, `x > 0 and x < 10` is true only if `x` is greater than 0 *and* at the same time, x is less than 10. How would you describe this in words? You would say that x is between 0 and 10, not including the endpoints. `n % 2 == 0 or n % 3 == 0` is true if *either* of the conditions is true, that is, if the number is divisible by 2 *or* divisible by 3. In this case, one, or the other, or both of the parts has to be true for the result to be true. Finally, the `not` operator negates a boolean expression, so `not x > y` is true if `x > y` is false, that is, if `x` is less than or equal to `y`. ```python x = 5 print(x > 0 and x < 10) n = 25 print(n % 2 == 0 or n % 3 == 0) ``` When trying to show how logical operators work, computer scientists and mathematicians alike will use **truth tables**. A truth table is a small table that lists all possible inputs on its left columns and then will display the output of its particular logical operator in the right column. Take the logical operator `and` for example: | | | | | --- | --- | --- | | **a** | **b** | **a** `and` **b** | | T | T | T | | T | F | F | | F | T | F | | F | F | F | The *T* in the table stands for `True` while the *F* stands for `False`. Notice that when **a** and **b** are both `True`, the logcial operator `and` outputs `True`. This is exactly how we normally use “and” in everyday conversation. Here are the rest of the operators: | | | | | | | | --- | --- | --- | --- | --- | --- | | **a** | **b** | **a** `and` **b** | **a** `or` **b** | `not` **a** | `not` **b** | | T | T | T | T | F | F | | T | F | F | T | F | T | | F | T | F | T | T | F | | F | F | F | F | T | T | Also, Google has provided this short video showing different logical operators: In the video, each letter is representative of a logical operater and only shows its color when the corresponding **x** or **y** is showing in the second **G**. If you take a look at xor, you will notice it is only colorful when either **x** or **y** is showing, but not both. This is called `exclusive or`, which we will not be using. WARNING! There is a very common mistake that occurs when programmers try to write boolean expressions. For example, what if we have a variable `number` and we want to check to see if its value is 5,6, or 7. In words we might say: “number equal to 5 or 6 or 7”. However, if we translate this into Python, `number == 5 or 6 or 7`, it will not be correct. The `or` operator must join the results of three equality checks. The correct way to write this is `number == 5 or number == 6 or number == 7`. This may seem like a lot of typing but it is absolutely necessary. You cannot take a shortcut. **Check your understanding** What is a correct Python expression for checking to see if a number stored in a variable x is between 0 and 5? * x > 0 and < 5 * Each comparison must be between exactly two values. In this case the right-hand expression < 5 lacks a value on its left. * x > 0 or x < 5 * Although this is legal Python syntax, the expression is incorrect. It will evaluate to true for all numbers that are either greater than 0 or less than 5. Because all numbers are either greater than 0 or less than 5, this expression will always be True. * x > 0 and x < 5 * Yes, with an and keyword both expressions must be true so the number must be greater than 0 an less than 5 for this expression to be true. Although most other programming languages do not allow this mathematical syntax, in Python, you could also write 0 < x < 5. Say you are registering for next semester’s classes. You have choice A, which is your art class, and choice B, which is your math class. You need both of them, but it’s a race between time and luck. If you end up registering on time for choice A, but you don’t get your choice B, which logical operators would be true? * A and B * Both A and B need to be True in order for this to be True. * A or B * Either A or B need to be True in order for this to be True. * not A * A needs to be False in order for this to be True. * not B * B needs to be False in order for this to be True. ### 7.2.1. Logical Opposites Each of the six relational operators has a logical opposite: for example, suppose we can get a driving licence when our age is greater or equal to 17, we can *not* get the driving licence when we are less than 17. | Operator | Definiton | Logical Opposites | | --- | --- | --- | | **==** | Equals to | **!=** | | **!=** | Not Equals to | **==** | | **<** | Less than | **>=** | | **<=** | Less Than or Equal to | **>** | | **>** | Greater Than | **<=** | | **>=** | Greater Than or Equal to | **<** | Understanding these logical opposites allows us to sometimes get rid of `not` operators. `not` operators are often quite difficult to read in computer code, and our intentions will usually be clearer if we can eliminate them. For example, if we wrote this Python: ```python if not (age >= 17): print("Hey, you're too young to get a driving licence!") ``` it would probably be clearer to use the simplification laws, and to write instead: ```python if age < 17: print("Hey, you're too young to get a driving licence!") ``` Two powerful simplification laws (called de Morgan’s laws) that are often helpful when dealing with complicated Boolean expressions are: ```python not (x and y) == (not x) or (not y) not (x or y) == (not x) and (not y) ``` For example, suppose you want to update your phone; however, your phone will only update if it has at least 50% battery life and 15% of its storage available. As we look at the Python code for this, we see: ```python if not ((phone_charge >= 0.50) and (phone_storage >= .15)): print("You cannot restart your phone. Battery too low or not enough free space.") else: print("Updating now...Several restarts may be required.") ``` Applying rules of logical opposites would let us rework the condition in a (perhaps) easier to understand way like this: ```python if (phone_charge < 0.50) or (phone_storage < .15): print("You cannot restart your phone. Battery too low or not enough free space.") else: print("Updating now...Several restarts may be required.") ``` We could also get rid of the `not` by swapping around the `then` and `else` parts of the conditional. So here is a third version, also equivalent: ```python if (phone_charge >= 0.50) and (phone_storage >= .15): print("Updating now...Several restarts may be required.") else: print("You cannot restart your phone. Battery too low or not enough free space.") ``` This version is probably the best of the three, because it very closely matches the initial English statement. Clarity of our code (for other humans), and making it easy to see that the code does what was expected should always be a high priority. As our programming skills develop we’ll find we have more than one way to solve any problem. So good programs are *designed*. We make choices that favour clarity, simplicity, and elegance. The job title *software architect* says a lot about what we do — we are *architects* who engineer our products to balance beauty, functionality, simplicity and clarity in our creations. ## 7.3. Precedence of Operators We have now added a number of additional operators to those we learned in the previous chapters. It is important to understand how these operators relate to the others with respect to operator precedence. Python will always evaluate the arithmetic operators first (\*\* is highest, then multiplication/division, then addition/subtraction). Next comes the relational operators. Finally, the logical operators are done last. This means that the expression `x*5 >= 10 and y-6 <= 20` will be evaluated so as to first perform the arithmetic and then check the relationships. The `and` will be done last. Although many programmers might place parenthesis around the two relational expressions, it is not necessary. The following table summarizes the precedence discussed so far from highest to lowest. See [Operator precedence table](../Appendices/PrecedenceTable.html#operator-summary) for *all* the operators introduced in this book. | Level | Category | Operators | | --- | --- | --- | | 7(high) | exponent | \*\* | | 6 | multiplication | \*,/,//,% | | 5 | addition | +,- | | 4 | relational | ==,!=,<=,>=,>,< | | 3 | logical | not | | 2 | logical | and | | 1(low) | logical | or | Note This workspace is provided for your convenience. You can use this activecode window to try out anything you like. **Check your understanding** Which of the following properly expresses the precedence of operators (using parentheses) in the following expression: 5\*3 > 10 and 4+6==11 * ((5\*3) > 10) and ((4+6) == 11) * Yes, \* and + have higher precedence, followed by > and ==, and then the keyword "and" * (5\*(3 > 10)) and (4 + (6 == 11)) * Arithmetic operators (\*, +) have higher precedence than comparison operators (>, ==) * ((((5\*3) > 10) and 4)+6) == 11 * This grouping assumes Python simply evaluates from left to right, which is incorrect. It follows the precedence listed in the table in this section. * ((5\*3) > (10 and (4+6))) == 11 * This grouping assumes that "and" has a higher precedence than ==, which is not true. Here is an animation for the above expression: Next Step Reset 5 \* 3 > 10 and 4 + 6 == 11 ## 7.4. Conditional Execution: Binary Selection In order to write useful programs, we almost always need the ability to check conditions and change the behavior of the program accordingly. **Selection statements**, sometimes also referred to as **conditional statements**, give us this ability. The simplest form of selection is the **if statement**. This is sometimes referred to as **binary selection** since there are two possible paths of execution. ```python x = 15 if x % 2 == 0: print(x, "is even") else: print(x, "is odd") ``` The syntax for an `if` statement looks like this: ```python if BOOLEAN EXPRESSION: STATEMENTS_1 # executed if condition evaluates to True else: STATEMENTS_2 # executed if condition evaluates to False ``` The boolean expression after the `if` statement is called the **condition**. If it is true, then the immediately following indented statements get executed. If not, then the statements indented under the `else` clause get executed. Flowchart of a **if** statement with an **else**  As with the function definition from the last chapter and other compound statements like `for`, the `if` statement consists of a header line and a body. The header line begins with the keyword `if` followed by a *boolean expression* and ends with a colon (:). The more indented statements that follow are called a **block**. Each of the statements inside the first block of statements is executed in order if the boolean expression evaluates to `True`. The entire first block of statements is skipped if the boolean expression evaluates to `False`, and instead all the statements under the `else` clause are executed. There is no limit on the number of statements that can appear under the two clauses of an `if` statement, but there has to be at least one statement in each block. Each compound statement includes a heading and all the following further-indented statements in the block after the heading. The `if` - `else` statement is an unusual compound statement because it has more than one part at the same level of indentation as the `if` heading, (the `else` clause, with its own indented block). Lab * [Approximating Pi with Simulation](../Labs/montepi.html) In this guided lab exercise we will work through a problem solving exercise related to approximating the value of pi using random numbers. **Check your understanding** How many statements can appear in each block (the if and the else) in a conditional statement? * Just one. * Each block may also contain more than one. * Zero or more. * Each block must contain at least one statement. * One or more. * Yes, a block must contain at least one statement and can have many statements. * One or more, and each must contain the same number. * The blocks may contain different numbers of statements. What does the following code print (choose from output a, b, c or nothing)? ```python if 4 + 5 == 10: print("TRUE") else: print("FALSE") ``` * TRUE * TRUE is printed by the if-block, which only executes if the conditional (in this case, 4+5 == 10) is true. In this case 5+4 is not equal to 10. * FALSE * Since 4+5==10 evaluates to False, Python will skip over the if block and execute the statement in the else block. * TRUE on one line and FALSE on the next * Python would never print both TRUE and FALSE because it will only execute one of the if-block or the else-block, but not both. * Nothing will be printed * Python will always execute either the if-block (if the condition is true) or the else-block (if the condition is false). It would never skip over both blocks. What does the following code print? ```python if 4 + 5 == 10: print("TRUE") else: print("FALSE") print("TRUE") ``` ```python a. TRUE b. TRUE FALSE c. FALSE TRUE d. TRUE FALSE TRUE ``` * Output a * Although TRUE is printed after the if-else statement completes, both blocks within the if-else statement print something too. In this case, Python would have had to have skipped both blocks in the if-else statement, which it never would do. * Output b * Because there is a TRUE printed after the if-else statement ends, Python will always print TRUE as the last statement. * Output c * Python will print FALSE from within the else-block (because 5+4 does not equal 10), and then print TRUE after the if-else statement completes. * Output d * To print these three lines, Python would have to execute both blocks in the if-else statement, which it can never do. ## 7.5. Omitting the else Clause: Unary Selection Flowchart of an **if** with no **else**  Another form of the `if` statement is one in which the `else` clause is omitted entirely. This creates what is sometimes called **unary selection**. In this case, when the condition evaluates to `True`, the statements are executed. Otherwise the flow of execution continues to the statement after the body of the `if`. ```python x = 10 if x < 0: print("The negative number ", x, " is not valid here.") print("This is always printed") ``` What would be printed if the value of `x` is negative? Try it. **Check your understanding** What does the following code print? ```python x = -10 if x < 0: print("The negative number ", x, " is not valid here.") print("This is always printed") ``` ```python a. This is always printed b. The negative number -10 is not valid here This is always printed c. The negative number -10 is not valid here ``` * Output a * Because -10 is less than 0, Python will execute the body of the if-statement here. * Output b * Python executes the body of the if-block as well as the statement that follows the if-block. * Output c * Python will also execute the statement that follows the if-block (because it is not enclosed in an else-block, but rather just a normal statement). * It will cause an error because every if must have an else clause. * It is valid to have an if-block without a corresponding else-block (though you cannot have an else-block without a corresponding if-block). Will the following code cause an error? ```python x = -10 if x < 0: print("The negative number ", x, " is not valid here.") else: print(x, " is a positive number") else: print("This is always printed") ``` * No * Every else-block must have exactly one corresponding if-block. If you want to chain if-else statements together, you must use the else if construct, described in the chained conditionals section. * Yes * This will cause an error because the second else-block is not attached to a corresponding if-block. ## 7.6. Nested conditionals One conditional can also be **nested** within another. For example, assume we have two integer variables, `x` and `y`. The following pattern of selection shows how we might decide how they are related to each other. ```python if x < y: print("x is less than y") else: if x > y: print("x is greater than y") else: print("x and y must be equal") ``` The outer conditional contains two branches. The second branch (the else from the outer) contains another `if` statement, which has two branches of its own. Those two branches could contain conditional statements as well. The flow of control for this example can be seen in this flowchart illustration.  Here is a complete program that defines values for `x` and `y`. Run the program and see the result. Then change the values of the variables to change the flow of control. ```python x = 10 y = 10 if x < y: print("x is less than y") else: if x > y: print("x is greater than y") else: print("x and y must be equal") ``` Note In some programming languages, matching the if and the else is a problem. However, in Python this is not the case. The indentation pattern tells us exactly which else belongs to which if. If you are still a bit unsure, here is the same selection as part of a codelens example. Step through it to see how the correct `print` is chosen. Activity: CodeLens 7.6.2 (sel1) **Check your understanding** Will the following code cause an error? ```python x = -10 if x < 0: print("The negative number ", x, " is not valid here.") else: if x > 0: print(x, " is a positive number") else: print(x," is 0") ``` * No * This is a legal nested if-else statement. The inner if-else statement is contained completely within the body of the outer else-block. * Yes * This is a legal nested if-else statement. The inner if-else statement is contained completely within the body of the outer else-block. ## 7.7. Chained conditionals Python provides an alternative way to write nested selection such as the one shown in the previous section. This is sometimes referred to as a **chained conditional**. ```python if x < y: print("x is less than y") elif x > y: print("x is greater than y") else: print("x and y must be equal") ``` The flow of control can be drawn in a different orientation but the resulting pattern is identical to the one shown above.  `elif` is an abbreviation of `else if`. Again, exactly one branch will be executed. There is no limit of the number of `elif` statements but only a single (and optional) final `else` statement is allowed and it must be the last branch in the statement. Each condition is checked in order. If the first is false, the next is checked, and so on. If one of them is true, the corresponding branch executes, and the statement ends. Even if more than one condition is true, only the first true branch executes. Here is the same program using `elif`. ```python x = 10 y = 10 if x < y: print("x is less than y") elif x > y: print("x is greater than y") else: print("x and y must be equal") ``` Note This workspace is provided for your convenience. You can use this activecode window to try out anything you like. **Check your understanding** Which of I, II, and III below gives the same result as the following nested if? ```python # nested if-else statement x = -10 if x < 0: print("The negative number ", x, " is not valid here.") else: if x > 0: print(x, " is a positive number") else: print(x, " is 0") ``` ```python I. if x < 0: print("The negative number ", x, " is not valid here.") else x > 0: print(x, " is a positive number") else: print(x, " is 0") ``` ```python II. if x < 0: print("The negative number ", x, " is not valid here.") elif x > 0: print(x, " is a positive number") else: print(x, " is 0") ``` ```python III. if x < 0: print("The negative number ", x, " is not valid here.") if x > 0: print(x, " is a positive number") else: print(x, " is 0") ``` * I only * You can not use a Boolean expression after an else. * II only * Yes, II will give the same result. * III only * No, III will not give the same result. The first if statement will be true, but the second will be false, so the else part will execute. * II and III * No, Although II is correct III will not give the same result. Try it. * I, II, and III * No, in I you can not have a Boolean expression after an else. What will the following code print if x = 3, y = 5, and z = 2? ```python if x < y and x < z: print("a") elif y < x and y < z: print("b") else: print("c") ``` * a * While the value in x is less than the value in y (3 is less than 5) it is not less than the value in z (3 is not less than 2). * b * The value in y is not less than the value in x (5 is not less than 3). * c * Since the first two Boolean expressions are false the else will be executed. ## 7.8. Boolean Functions We have already seen that boolean values result from the evaluation of boolean expressions. Since the result of any expression evaluation can be returned by a function (using the `return` statement), functions can return boolean values. This turns out to be a very convenient way to hide the details of complicated tests. For example: ```python def isDivisible(x, y): if x % y == 0: result = True else: result = False return result print(isDivisible(10, 5)) ``` The name of this function is `isDivisible`. It is common to give **boolean functions** names that sound like yes/no questions. `isDivisible` returns either `True` or `False` to indicate whether the `x` is or is not divisible by `y`. We can make the function more concise by taking advantage of the fact that the condition of the `if` statement is itself a boolean expression. We can return it directly, avoiding the `if` statement altogether: ```python def isDivisible(x, y): return x % y == 0 ``` Boolean functions are often used in conditional statements: ```python if isDivisible(x, y): ... # do something ... else: ... # do something else ... ``` It might be tempting to write something like `if isDivisible(x, y) == True:` but the extra comparison is redundant. You only need an `==` expression if you are comparing some other type than boolean. (`isDivisible(x, y) == False` can also be made more concise as `not isDivisible(x, y)`). The following example shows the `isDivisible` function at work. Notice how descriptive the code is when we move the testing details into a boolean function. Try it with a few other actual parameters to see what is printed. ```python def isDivisible(x, y): return x % y == 0 if isDivisible(10, 5): print("That works") else: print("Those values are no good") ``` Here is the same program in codelens. When we evaluate the `if` statement in the main part of the program, the evaluation of the boolean expression causes a call to the `isDivisible` function. This is very easy to see in codelens. Activity: CodeLens 7.8.3 (ch06\_boolcodelens) **Check your understanding** What is a Boolean function? * A function that returns True or False * A Boolean function is just like any other function, but it always returns True or False. * A function that takes True or False as an argument * A Boolean function may take any number of arguments (including 0, though that is rare), of any type. * The same as a Boolean expression * A Boolean expression is a statement that evaluates to True or False, e.g. 5+3==8. A function is a series of expressions grouped together with a name that are only executed when you call the function. Is the following statement legal in a Python function (assuming x, y and z are defined to be numbers)? ```python return x + y < z ``` * Yes * It is perfectly valid to return the result of evaluating a Boolean expression. * No * x +y < z is a valid Boolean expression, which will evaluate to True or False. It is perfectly legal to return True or False from a function, and to have the statement to be evaluated in the same line as the return keyword. ### 7.8.1. More Unit Testing When we write unit tests, we should also consider **output equivalence classes** that result in significantly different results. The `isDivisible` function can return either `True` or `False`. These two different outputs give us two equivalence classes. We then choose inputs that should give each of the different results. **It is important to have at least one test for each output equivalence class.** ```python def isDivisible(x, y): '''is x evenly divisible by y?''' return x % y == 0 if __name__ == "__main__": import test ``` Extend the program … Starting on line 7, write two unit tests (that should pass), one for each output equivalence class. Note This workspace is provided for your convenience. You can use this activecode window to try out anything you like. ## 7.9. Glossary block : A group of consecutive statements with the same indentation. body : The block of statements in a compound statement that follows the header. boolean expression : An expression that is either true or false. boolean function : A function that returns a boolean value. The only possible values of the `bool` type are `False` and `True`. boolean value : There are exactly two boolean values: `True` and `False`. Boolean values result when a boolean expression is evaluated by the Python interepreter. They have type `bool`. branch : One of the possible paths of the flow of execution determined by conditional execution. chained conditional : A conditional branch with more than two possible flows of execution. In Python chained conditionals are written with `if ... elif ... else` statements. comparison operator : One of the operators that compares two values: `==`, `!=`, `>`, `<`, `>=`, and `<=`. condition : The boolean expression in a conditional statement that determines which branch is executed. conditional statement : A statement that controls the flow of execution depending on some condition. In Python the keywords `if`, `elif`, and `else` are used for conditional statements. logical operator : One of the operators that combines boolean expressions: `and`, `or`, and `not`. modulus operator : An operator, denoted with a percent sign ( `%`), that works on integers and yields the remainder when one number is divided by another. nesting : One program structure within another, such as a conditional statement inside a branch of another conditional statement. ## 7.10. Exercises 1. What do these expressions evaluate to? 1. `3 == 3` 2. `3 != 3` 3. `3 >= 4` 4. `not (3 < 4)` 1. True 2. False 3. False 4. False [Show Comments](#disqus_thread) 2. Give the **logical opposites** of these conditions. You are not allowed to use the `not` operator. 1. `a > b` 2. `a >= b` 3. `a >= 18 and day == 3` 4. `a >= 18 or day != 3` 3. Write a function which is given an exam mark, and it returns a string — the grade for that mark — according to this scheme: | Mark | Grade | | --- | --- | | >= 90 | A | | [80-90) | B | | [70-80) | C | | [60-70) | D | | < 60 | F | The square and round brackets denote closed and open intervals. A closed interval includes the number, and open interval excludes it. So 79.99999 gets grade C , but 80 gets grade B. Test your function by printing the mark and the grade for a number of different marks. ```python def getGrade(grade): #your code here ===== from unittest.gui import TestCaseGui class myTests(TestCaseGui): def testOne(self): self.assertEqual(getGrade(95),'A','Tested getGrade on input of 95') self.assertEqual(getGrade(85),'B','Tested getGrade on input of 85') self.assertEqual(getGrade(65),'D','Tested getGrade on input of 65') self.assertEqual(getGrade(79.99999),'C','Tested getGrade on input of 79.9999') self.assertEqual(getGrade(80),'B','Tested getGrade on input of 80') myTests().main() ``` ```python def grade(mark): if mark >= 90: return "A" else: if mark >= 80: return "B" else: if mark >= 70: return "C" else: if mark >= 60: return "D" else: return "F" mark = 83 print( "Mark:", str(mark), "Grade:", grade(mark)) ``` [Show Comments](#disqus_thread) 4. Modify the turtle bar chart program from the previous chapter so that the bar for any value of 200 or more is filled with red, values between [100 and 200) are filled yellow, and bars representing values less than 100 are filled green. 5. In the turtle bar chart program, what do you expect to happen if one or more of the data values in the list is negative? Go back and try it out. Change the program so that when it prints the text value for the negative bars, it puts the text above the base of the bar (on the 0 axis). ```python import turtle def drawBar(t, height): """ Get turtle t to draw one bar, of height. """ t.begin_fill() # start filling this shape if height < 0: t.write(str(height)) t.left(90) t.forward(height) if height >= 0: t.write(str(height)) t.right(90) t.forward(40) t.right(90) t.forward(height) t.left(90) t.end_fill() # stop filling this shape xs = [48, -50, 200, 240, 160, 260, 220] # here is the data maxheight = max(xs) minheight = min(xs) numbars = len(xs) border = 10 tess = turtle.Turtle() # create tess and set some attributes tess.color("blue") tess.fillcolor("red") tess.pensize(3) wn = turtle.Screen() # Set up the window and its attributes wn.bgcolor("lightgreen") if minheight > 0: lly = 0 else: lly = minheight - border wn.setworldcoordinates(0-border, lly, 40*numbars+border, maxheight+border) for a in xs: drawBar(tess, a) wn.exitonclick() ``` [Show Comments](#disqus_thread) 6. Write a function `findHypot`. The function will be given the length of two sides of a right-angled triangle and it should return the length of the hypotenuse. (Hint: `x ** 0.5` will return the square root, or use `sqrt` from the math module) ```python def findHypot(a,b): # your code here ===== from unittest.gui import TestCaseGui class myTests(TestCaseGui): def testOne(self): self.assertEqual(findHypot(12.0,5.0),13.0,"Tested findHypot on inputs of 12.0 and 5.0") self.assertEqual(findHypot(14.0,48.0),50.0,"Tested findHypot on inputs of 14.0 and 48.0") self.assertEqual(findHypot(21.0,72.0),75.0,"Tested findHypot on inputs of 21.0 and 72.0") self.assertAlmostEqual(findHypot(1,1.73205),1.999999,2,"Tested findHypot on inputs of 1 and 1.73205") myTests().main() ``` 7. Write a function called `is_even(n)` that takes an integer as an argument and returns `True` if the argument is an **even number** and `False` if it is **odd**. ```python def is_even(n): # your code here ===== from unittest.gui import TestCaseGui class myTests(TestCaseGui): def testOne(self): self.assertEqual(is_even(10),True,"Tested is_even on input of 10") self.assertEqual(is_even(5),False,"Tested is_even on input of 5") self.assertEqual(is_even(1),False,"Tested is_even on input of 1") self.assertEqual(is_even(0),True,"Tested is_even on input of 0") myTests().main() ``` ```python from test import testEqual def is_even(n): if n % 2 == 0: return True else: return False testEqual(is_even(10), True) testEqual(is_even(5), False) testEqual(is_even(1), False) testEqual(is_even(0), True) ``` [Show Comments](#disqus_thread) 8. Now write the function `is_odd(n)` that returns `True` when `n` is odd and `False` otherwise. ```python def is_odd(n): # your code here ===== from unittest.gui import TestCaseGui class myTests(TestCaseGui): def testOne(self): self.assertEqual(is_odd(10),False,"Tested is_odd on input of 10") self.assertEqual(is_odd(5),True,"Tested is_odd on input of 5") self.assertEqual(is_odd(1),True,"Tested is_odd on input of 1") self.assertEqual(is_odd(0),False,"Tested is_odd on input of 0") myTests().main() ``` 9. Modify `is_odd` so that it uses a call to `is_even` to determine if its argument is an odd integer. ```python def is_odd(n): # your code here ===== from unittest.gui import TestCaseGui class myTests(TestCaseGui): def testOne(self): self.assertEqual(is_odd(10),False,"Tested is_odd on input of 10") self.assertEqual(is_odd(5),True,"Tested is_odd on input of 5") self.assertEqual(is_odd(1),True,"Tested is_odd on input of 1") self.assertEqual(is_odd(0),False,"Tested is_odd on input of 0") myTests().main() ``` ```python from test import testEqual def is_even(n): if n % 2 == 0: return True else: return False def is_odd(n): if is_even(n): return False else: return True testEqual(is_odd(10), False) testEqual(is_odd(5), True) testEqual(is_odd(1), True) testEqual(is_odd(0), False) ``` [Show Comments](#disqus_thread) 10. Write a function `is_rightangled` which, given the length of three sides of a triangle, will determine whether the triangle is right-angled. Assume that the third argument to the function is always the longest side. It will return `True` if the triangle is right-angled, or `False` otherwise. Hint: floating point arithmetic is not always exactly accurate, so it is not safe to test floating point numbers for equality. If a good programmer wants to know whether `x` is equal or close enough to `y`, they would probably code it up as ```python if abs(x - y) < 0.001: # if x is approximately equal to y ... ``` ```python def is_rightangled(a, b, c): # your code here ===== from unittest.gui import TestCaseGui class myTests(TestCaseGui): def testOne(self): self.assertEqual(is_rightangled(1.5,2.0,2.5),True,"Tested is_rightangled on inputs of 1.5, 2.0 and 2.5") self.assertEqual(is_rightangled(4.0,8.0,16.0),False,"Tested is_rightangled on inputs of 4.0, 8.0 and 16.0") self.assertEqual(is_rightangled(4.1,8.2,9.1678787077),True,"Tested is_rightangled on inputs of 4.1, 8.2 and 9.1678787077") self.assertEqual(is_rightangled(4.1,8.2,9.16787),True,"Tested is_rightangled on inputs of 4.1, 8.2, and 9.16787") self.assertEqual(is_rightangled(4.1,8.2,9.168),False,"Tested is_rightangled on inputs of 4.1, 8.2 and 9.168") self.assertEqual(is_rightangled(0.5,0.4,0.64031),True,"Tested is_rightangled on inputs of 0.5, 0.4 and 0.64031") myTests().main() ``` 11. Extend the above program so that the sides can be given to the function in any order. ```python def is_rightangled(a, b, c): # your code here ===== from unittest.gui import TestCaseGui class myTests(TestCaseGui): def testOne(self): self.assertEqual(is_rightangled(1.5,2.5,2.0),True,"Tested is_rightangled on inputs of 1.5, 2.5 and 2.0") self.assertEqual(is_rightangled(16.0,4.0,8.0),False,"Tested is_rightangled on inputs of 16.0, 4.0 and 8.0") self.assertEqual(is_rightangled(4.1,8.2,9.1678787077),True,"Tested is_rightangled on inputs of 4.1, 8.2 and 9.1678787077") self.assertEqual(is_rightangled(4.1,9.16787,8.2),True,"Tested is_rightangled on inputs of 4.1, 9.16787 and 8.2") self.assertEqual(is_rightangled(4.1,8.2,9.168),False,"Tested is_rightangled on inputs of 4.1, 8.2 and 9.168") self.assertEqual(is_rightangled(0.5,0.64031,0.4),True,"Tested is_rightangled on inputs of 0.5, 0.64031 and 0.4") myTests().main() ``` ```python from test import testEqual def is_rightangled(a, b, c): is_rightangled = False if a > b and a > c: is_rightangled = abs(b**2 + c**2 - a**2) < 0.001 elif b > a and b > c: is_rightangled = abs(a**2 + c**2 - b**2) < 0.001 else: is_rightangled = abs(a**2 + b**2 - c**2) < 0.001 return is_rightangled testEqual(is_rightangled(1.5, 2.0, 2.5), True) testEqual(is_rightangled(4.0, 8.0, 16.0), False) testEqual(is_rightangled(4.1, 8.2, 9.1678787077), True) testEqual(is_rightangled(4.1, 8.2, 9.16787), True) testEqual(is_rightangled(4.1, 8.2, 9.168), False) testEqual(is_rightangled(0.5, 0.4, 0.64031), True) ``` [Show Comments](#disqus_thread) 12. 3 criteria must be taken into account to identify leap years: The year is evenly divisible by 4; If the year can be evenly divided by 100, it is NOT a leap year, unless; The year is also evenly divisible by 400. Then it is a leap year. Write a function that takes a year as a parameter and returns `True` if the year is a leap year, `False` otherwise. ```python def isLeap(year): # your code here ===== from unittest.gui import TestCaseGui class myTests(TestCaseGui): def testOne(self): self.assertEqual(isLeap(1944),True,"Tested isLeap on an input of 1944") self.assertEqual(isLeap(2011),False,"Tested isLeap on an input of 2011") self.assertEqual(isLeap(1986),False,"Tested isLeap on an input of 1986") self.assertEqual(isLeap(1800),False,"Tested isLeap on an input of 1800") self.assertEqual(isLeap(1900),False,"Tested isLeap on an input of 1900") self.assertEqual(isLeap(2000),True,"Tested isLeap on an input of 2000") self.assertEqual(isLeap(2056),True,"Tested isLeap on an input of 2056") myTests().main() ``` 13. Implement the calculator for the date of Easter. The following algorithm computes the date for Easter Sunday for any year between 1900 to 2099. Ask the user to enter a year. Compute the following: > 1. a = year % 19 > 2. b = year % 4 > 3. c = year % 7 > 4. d = (19 \* a + 24) % 30 > 5. e = (2 \* b + 4 \* c + 6 \* d + 5) % 7 > 6. dateofeaster = 22 + d + e Special note: The algorithm can give a date in April. Also, if the year is one of four special years (1954, 1981, 2049, or 2076) then subtract 7 from the date. Your program should print an error message if the user provides a date that is out of range. ```python year = int(input("Please enter a year")) if year >= 1900 and year <= 2099: a = year % 19 b = year % 4 c = year % 7 d = (19*a + 24) % 30 e = (2*b + 4*c + 6*d + 5) % 7 dateofeaster = 22 + d + e if year == 1954 or year == 2981 or year == 2049 or year == 2076: dateofeaster = dateofeaster - 7 if dateofeaster > 31: print("April", dateofeaster - 31) else: print("March", dateofeaster) else: print("ERROR...year out of range") ``` [Show Comments](#disqus_thread)