Predict what Python will print in response to each of these expressions. Then try it and make sure your answer was correct, or if not, that you understand why!
a = 1 b = a + 1 a + b + a * b ______________ a == b ______________ z, y = 1, 2 print(z) ______________ def square(x): x * x a = square(b) print(a) ______________ def square(y): return y * y a = square(b) print(a) _______________
Predict what Python will print in response to each of these expressions. Then try it and make sure your answer was correct, or if not, that you understand why!
boolean operatorsa > b and a == 0 _______________ a > b or a == 0 _______________ not a == 0 _______________if statements
if a==b: a else: b _______________ if a == 4: 6 elif b >= 4: 6 + 7 + a else: 25 ________________ if b != a: a; b #return two different values _________________while loops
n = 4 def countDown(n): while n > 0: print(n) n = n - 1 countDown(5) __________________
For the next three exercises, you will be using emacs. Create a file, lab2.py and put the following three function definitions in this file. We will have you print this file at the end.
Define a procedure, max(a, b) that takes in two numbers as arguments and returns the maximum of those two numbers without using the builtin max function.
Write a procedure expt(base, power) which implements the exponent function. For example, calling expt(3, 2) should return 9, and calling expt(2, 3) should return 8.
Before we write our next function, let's look at the idea of integer division versus normal division.
Normal Division Integer Division
# a / b → returns a float! # a // b → rounds down to the nearest integer >>> 1/4 >>> 1//4 0.25 0 >>> 4/2 >>> 4//2 2.0 2 >>> 5/3 >>> 5//3 1.666666666667 1
Thus, if we have a function "%" that gives us the remainder of dividing two numbers, we can see that the following rule applies:
b * (a // b) + (a % b) = a
Now, define a procedure factors(n) which takes in a number, n, and prints out all of the numbers that divide n evenly. For example, a call with n=20 should result as follows (order doesn’t matter):
>>> factors(20) 20 10 5 4 2 1
Helpful Tip: You can use the % to find if something divides evenly into a number. % gives you a remainder, as follows:
>>> 10 % 5 0 >>> 10 % 4 2 >>> 10 % 7 3 >>> 10 % 2 0
Use the lpr command to print your lab2.py file. To do this, type the following command into your main xterm window (not Emacs or Python):
lpr lab2.py
This will print the lab2 file in the room opposite from 273 Soda. You could print other files by issuing a similar command. You have 200 free pages under your account.
At the end of last lab, we introduced the ucb.py file which the staff have provided and contains several helpful features in writing code. We started with main which helps with testing.
In order to use any features of the ucb.py file, you need to import it into your Python files by including an import statement at the top of each file:
from ucb import main, trace, interact
An entry point of a program is the place where execution starts happening. It's usually very convenient to be able to demarcate an entry point in a Python file for testing purposes. Say we have the following file cube.py:
def cube(x): return x * x * x print("Should be 1:", cube(1)) print("Should be 8:", cube(2)) print("Should be 27:", cube(3)) star [123] ~ # python cube.py Should be 1: 1 Should be 8: 8 Should be 27: 27
One problem with this file is that the tests aren't cleanly arranged. Plus, if I'm using Python in Emacs, then the test output will print out every time I re-load the cube.py file, which we might not want.
Note: The above interaction shows how to run Python scripts from the command line. When you do python cube.py, Python will effectively evaluate each line of the file, which is why the print outputs show when we run cube.py.
Instead, it'd be much better if we had a test function that performed these tests:
def cube(x): return x * x * x def run_tests(): print("Should be 1:", cube(1)) print("Should be 8:", cube(2)) print("Should be 27:", cube(3))
However, now, if I run the file, nothing happens:
star [123] ~ # python cube.py star [124] ~ #
This is because, to Python, all we've done is define two functions: cube, and run_tests. We want Python to actually do something when we do 'python cube.py', however. So, we can specify an entry point with the @main annotation:
from ucb import main, trace, interact def cube(x): return x * x * x def run_tests(): print("Should be 1:", cube(1)) print("Should be 8:", cube(2)) print("Should be 27:", cube(3)) @main def main(): print("Starting.") run_tests() print("Ending.") star [123] ~ # python cube.py Starting. Should be 1: 1 Should be 8: 8 Should be 27: 27 Ending.
Let's face it - people make mistakes, especially when writing code. When trying to track down a mistake, it's usually useful to be able to see what a particular function does every time it's called. This is called trace-ing a function.
Say we have the following (incorrect) code to compute the Euclidean distance between two points:
def square(x): return 2 * x def distance(x1,x2,y1,y2): return sqrt(square(x1-x2) + square(y1-y2))
We call the distance function with the points (3,0) and (0,0), expecting to get 3.0 back, but instead we get:
>>> distance(3,0,0,0) 2.449489742783178
Hm, that's not right! We take another look at the distance function, and everything seems to be structured correctly. So, the problem could be with one of the helper functions called by distance - we'll first examine square. We can trace square to see how square behaves when called by adding an '@trace' above the square definition: (Remember to import the ucb features)
from ucb import main, trace, interact from math import sqrt @trace def square(x): return 2 * x def distance(x1,x2,y1,y2): return sqrt(square(x1-x2) + square(y1-y2))
Then, when we call distance, we get:
>>> distance(3,0,0,0) square(3): square(3) -> 6 square(0): square(0) -> 0 2.449489742783178
The trace output tells us when a trace'd function is called (i.e. 'square(3):' means that the square function was called with argument 3), and it also tells us what a trace'd function returns (i.e. 'square(3) -> 6' means that square(3) returned 6).
interact is a utility function in ucb.py that, when called, stops execution of the program, and opens an interactive prompt at that point in the program. This is very useful for debugging, because at this prompt, you can examine variable values during program execution. For instance, copy the following into a new file dist_interact.py
from ucb import main, trace, interact
def distance(x1,y1,x2,y2): a = (x2-x1)**2 b = (y2-y1)**2 c = a+b print("== Before interact ==") interact() print("== After interact ==") return sqrt(c) @main def run(): print("Starting.") print(distance(1, 1, 3, 1)) print("Done!")
Run the program (either in Emacs, or in the terminal by doing 'python dist_interact.py'). You'll notice that the '>>>' Python prompt will appear. We are currently in the middle of evaluating the body of the distance function - in fact, we've stopped exactly where the interact() function is called. Print out the values of the local variables (such as x1, y2, a, b, c) to see their values. To resume execution, do C-d (if you're on a Windows machine, you instead need to do C-z Return).
interact() is useful for debugging, because you get to halt execution and examine variables to see what went awry.
Fin.