By the end of this module you will be able to:
The numbers 1, 4, 9, 16, 25, and so on are squares, with the next one being 36 (which is 62).
Suppose we want to identify the largest square that's less than 1000.
We'll first show how this can be solved with a new kind of loop: the while-loop, and then try the same problem with a for-loop.
The program:
k = 1 while k*k < 1000: k = k + 1 # Reduce by 1 because now k*k > 1000 k = k - 1 print('largest square < 1000:', k*k, '= square of', k)
4.1 Exercise: Type up the above in my_while_example.py.
How does a while-loop work?
while k*k < 1000:Thus, as long as the value of k is such that k*k is less than 1000, execution enters and stays inside the loop.
while k*k < 1000: k = k + 1
Let's examine a simpler while-loop:
k = 1 while k < 6: print(k) k = k + 1
4.2 Exercise: Trace the execution of each iteration in your module pdf. Then confirm by typing up the above in my_while_example2.py.
4.3 Exercise: Consider this variation:
k = 7 while k < 6: print(k) k = k + 1Trace the execution of the above program in your module pdf. Then confirm by typing up the above in my_while_example3.py.
4.4 Exercise: Consider this variation:
k = 1 while k < 6: print(k)Trace the execution of a few iterations of the above program in your module pdf. Then confirm by typing up the above in my_while_example4.py. After a minute, you may need to terminate the execution of the program by hand (by getting rid of the window itself).
4.5 Audio:
Keep in mind:
k = 1 while k < 6: print(k) k = k + 1
k = 1 while k < 6: print(k) k = k + 1
k = 1 while k < 6: print(k) i = k + 1 # Error!Here
k = 6 while k < 6: print(k) k = k + 1The code still runs but the while-loop is not executed at all.
Consider this example:
x = 0.5 s = 0 while s <= 2: s = s + x print('s =', s)
4.6 Exercise: Try to guess the output before confirming in my_while_example5.py.
Note:
Let's look at a more illustrative version now:
x = 0.5 s = 0 k = 0 while s <= 2: s = s + x k = k + 1 print('s =', s, 'k =', k)Here, we've added a counter variable to track each loop iteration.
4.7 Exercise: Trace the values of the three variables in your module pdf. Then confirm in my_while_example6.py.
So far it's been straightforward. Let's now solve a problem:
x = 1 s = 0 k = 0 while s < 0.9: x = x / 2 # Halve x each time s = s + x k = k +1 print(k)
4.8 Exercise: Trace the values of the three variables in your module pdf. Then confirm in my_while_example7.py.
4.9 Audio:
A few comments that go beyond the scope of the course (just for curiosity):
while s < 0.9:to
while s < 1:
Let's contrast for-loops and while-loops by writing a for-loop as a while-loop, and vice-versa.
As an example, let's print the numbers 0 through 10:
# for-loop version for k in range(11): print(k) # while-loop version k = 0 while k < 11: print(k) k = k + 1
Note:
k = 0 while k < 11: print(k) k = k + 1
k = 0 while k < 11: print(k) k = k + 1
k = 0 while k < 11: print(k) k = k + 1
k = 0 while k < 11: print(k)This loop runs forever!
4.10 Exercise: Consider this for-loop:
for k in range(5, 20, 2): print(k)In my_for_while.py, write the while-loop equivalent.
Next, let's go from while to for:
s = 'hello' k = len(s) - 1 while k >= 0: print(s[k]) k = k - 1Note:
k = len(s) - 1
for k in range(len(s)-1, -1, -1): print(s[k])Here:
for k in range(len(s)-1, -1, -1):
for k in range(len(s)-1, -1, -1):
4.11 Exercise: Trace through both loops in your module pdf before confirming both in my_for_while2.py.
4.12 Exercise: Consider the following program with a while-loop:
def func(A): k = 0 while A[k].startswith('h') and (len(A[k]) > 4) and (k < len(A)): print(A[k]) k += 1 def func2(A): # Write your for-equivalent here: B = ['hello', 'hey there', 'howdy', 'huzzah', 'hi', 'greetings'] func(B) func2(B)Trace through the first loop (in func) in your module pdf, to see that the loop keeps printing strings from a list as long as the strings start with 'h' and have length at least 5. Then, in my_for_while3.py, complete the code in func2 using a for-loop to achieve the same result.
4.13 Video:
Let's return to our first example of finding the last square that's less than 1000.
Recall what we wrote:
k = 1 while k*k < 1000: k = k + 1 k = k - 1 print('largest square < 1000:', k*k, '= square of', k)
One can use a break statement as an alternative to writing the "loop exit" condition as the while condition.
We'll first do this with a for-loop, and then see something unusual with the while-loop version.
To simplify tracing, let's rephrase to "largest square less than 50".
First, the for-loop version:
for k in range(1, 50): # print('Before-if: k =', k) if k*k > 50: break # print('After-if: k =', k) k = k - 1 print(k)
4.14 Exercise: Type up the above in my_break.py, removing the # to un-comment the print's, so that you can see exactly what happens when the if-condition triggers.
Let's point out:
for k in range(10): print(k) breakThis would cause the first value (0) to print, and a break right out of the loop.
for k in range(1, 50): if k*k > 50: breakAfter all, as k gets close to 50, there is no way k*k would be less than 50. However, we'll leave it as is, for the sake of simplicity.
for k in range(1, 50): if (k+1)*(k+1) > 50: print(k) breakIs this more elegant, if a bit harder to understand at first?
4.15 Exercise: Trace the execution of the above loop in you module pdf, The type it up in my_break2.py, to confirm.
Next, let's look at a while-loop version of the original
k = 1 while True: if k*k > 50: break k = k + 1 k = k - 1 print(k)
4.16 Exercise: In your module pdf, describe what would go wrong if the statement k = k + 1 was mistakenly typed in as k = k - 1.
4.17 Exercise: In my_break3.py, go back to the earlier exercise (4.12) where you wrote a while-loop to print strings of length at least 5, and starting with 'h'. Rewrite the while-loop to use a break-statement instead.
Just as we've seen nested for-loops, so can we have nested while-loops or one kind inside another.
Consider this example:
m = 10 while m <= 10000: for k in range(1, m): if (k+1)*(k+1) >= m: print('largest square <', m, ':',k*k) break m = m * 10
4.19 Exercise: Type the above in my_nested_loop.py and try to make sense of it.
Let's explain:
4.20 Exercise: In my_nested_loop2.py, change the inner loop to a while loop so that we get the same output.
4.21 Exercise: In my_nested_loop3.py, change the code so that both the outerloop and innerloop are for-loops. One way to solve this problem: use a variable called j to range through the outer for-loop and then ensure that the innerloop executes only when j happens to equal m. (Aren't you glad we have while-loops?)
Consider the following problem:
One way to think about this problem "statistically" is this:
Instead of calculating by hand, we will write a program to estimate this number:
import random num_trials = 1000 total = 0 for k in range(num_trials): got_three = False num_three_flips = 0 while not got_three: c1 = random.choice(['H','T']) c2 = random.choice(['H','T']) c3 = random.choice(['H','T']) num_three_flips += 1 if (c1 == 'H') and (c2 == 'H') and (c3 == 'H'): got_three = True total += num_three_flips estimate = total / num_trials print('estimate', estimate)
4.23 Exercise: Type up the above in my_coin_flips.py to get an estimate. (Submit the program with 1000 trials.) Then, increase the number of trials to get a more accurate estimate, and report that in your module pdf. Can you explain the result intuitively?
Let's point out:
num_trials = 1000 total = 0 for k in range(num_trials): # how many of these resulted in successes? estimate = total / num_trials
4.24 Exercise: In my_coin_flips2.py, instead of using a flag variable, use a break statement to exit the loop. You might have to make a small adjustment to get the correct result.
4.25 Exercise: Consider an experiment where you roll a die twice to see if you get 6 and 6. In my_dierolls.py, estimate the average number of experiments needed to get two sixes.
Very often, data is collected and stored in files, and so it's desirable to learn how write code that plucks data right out of such files.
Let's start with a simple test file of plain text.
First, examine the file testfile.txt to see that it's a file consisting of four lines of text. (From the poet Ogden Nash.)
We will look at a few different versions of reading from this file.
Here's the first:
with open('testfile.txt', 'r') as in_file: lines = in_file.read() print(type(lines)) print(lines)
4.27 Exercise: Type up the above in my_file_read.py. What is the type of the variable lines? Note:
with open('testfile.txt', 'r') as in_file:
with open('testfile.txt', 'r') as in_file:
with open('testfile.txt', 'r') as in_file:
with open('testfile.txt', 'r') as in_file: lines = in_file.read()
Accordingly, let's look at a way to read the file into a list of strings, where each line is one string in the list:
lines = [] with open('testfile.txt', 'r') as in_file: line = in_file.readline() while line != '': lines.append(line.strip()) line = in_file.readline() print(type(lines)) print(lines)
4.28 Exercise: Type up the above in my_file_read2.py. What is the type of the variable lines?
Note:
Writing to a file:
with open('testcopy.txt', 'w') as out_file: for line in lines: out_file.write(line + '\n')
with open('testcopy.txt', 'w') as out_file: for line in lines: out_file.write(line + '\n')
with open('testcopy.txt', 'w') as out_file: for line in lines: out_file.write(line + '\n')Here, we're looping through the list, writing each string as one line in the file.
print('hello' + 'world') # Prints helloworld on one line print('hello' + '\n' + 'world') # Prints hello, and then world on the next line
4.29 Exercise: In my_file_readwrite.py, combine the reading and writing so that the program as whole results in copying from testfile.txt to testcopy.txt.
Next, let's read from a file of numbers and perform some basic stats:
data = [] with open('data.txt', 'r') as in_file: line = in_file.readline() while line != '': s = line.strip() # Remove leading/trailing whitespace x = float(s) # Convert string to float data.append(x) # Add to our list line = in_file.readline() # Get the next line print(data)
4.30 Exercise: In my_file_data.py, add code to compute the average of the numbers and print it. Compute the total as you iterate in the while-loop.
Consider a data file that looks like this, with three numbers on each line:
6.0 6.0 9.0 4 6 8 24 16 2 3 3.0 3 0.1 0.5 0.3What we'd like to do is compute the average of the numbers in each line. So, the output should be something like:
Average of 6.0 6.0 9.0 is: 7.0 Average of 4.0 6.0 8.0 is: 6.0 Average of 24.0 16.0 2.0 is: 14.0 Average of 3.0 3.0 3.0 is: 3.0 Average of 0.1 0.5 0.3 is: 0.3Therefore, what need to do is not only read a line at a time, but be able to extract multiple items from within a line.
We can split a string as follows:
s = '6.0 6.0 9.0' data = s.split() # data is a list print(data)
s = '6.0 6.0 9.0' data = s.split() # data is a list print(data) x = float(data[0]) y = float(data[1]) z = float(data[2]) # x, y, z are numbers avg = (x + y + z) / 3.0 print(avg)
4.32 Exercise: Type up the above in my_split_example.py to see. Next, examine the file data2.txt in a text editor and try and identify all the (unnecessary) whitespace within.
We'll now tackle one additional complication:
with open('data2.txt','r') as in_file: line = in_file.readline() while line != None: line = line.strip() print('[', line, ']', sep='') if len(line) == 0: break data = line.split() print(data) x = float(data[0]) y = float(data[1]) z = float(data[2]) avg = (x + y + z) / 3.0 print('Average of ', x, ' ', y, ' ', z, ' is: ', avg, sep='') line = in_file.readline()
4.33 Exercise: Type up the above in my_file_data2.py. You already have saved the file data2.txt in the same folder.
We'll point out a few things:
line = line.strip() print('[', line, ']', sep='') data = line.split() print(data)
print('[', line, ']', sep='')This is a common programming technique when you want to identify whitespace: put something around it that is actually visible.
Let's return to a problem we've seen before: identifying the longest sentence in a text file.
Take a moment to review that section
Observe:
import wordtool as wt def get_longest_sentence(filename): # Initiate the reading of the file sentences = wt.open_file_bysentence(filename) maxL = 0 # Get first sentence s = wt.next_sentence() while s != None: if len(s) > maxL: # Possibly update maxS maxL = len(s) maxS = s s = wt.next_sentence() # next one return maxS book = 'federalist_papers.txt' s = get_longest_sentence(book) print('Longest sentence in', book, 'with', len(s), 'chars:\n', s)
4.34 Exercise: In my_text_analysis.py, count the number of sentences that have length greater than 280 characters (more than a tweet) in federalist_papers.txt. You will need to download wordtool.py and wordsWithPOS.txt.
But of course. We're somehow going to combine while-loops with our recurring themes (algorithmic art, randomness).
This time, we will use a well-known idea from science called a random walk:
import random from drawtool import DrawTool dt = DrawTool() dt.set_XY_range(-5,5, -5,5) dt.set_aspect('equal') step = 1 def do_walk(max_steps): x = 0 y = 0 num_steps = 0 dt.draw_point(x, y) while num_steps < max_steps: direction = random.choice(['N','S','W','E']) if direction == 'N': y += step elif direction == 'S': y -= step elif direction == 'E': x += step else: x -= step dt.draw_point(x, y) print(direction, x, y) num_steps += 1 do_walk(5) dt.display()
4.35 Exercise: Type up the above in my_randomwalk.py and try out a larger number of steps. You will also need drawtool.py. Call do_walk() twice and change the draw color in between using dt.set_color('r').
Instead of running the random walk for a fixed number of steps, we'll now run the random walk until it "hits" one of the sides and stop.
while True: # get a random direction and move # if we hit one of the sides, then break
4.36 Exercise: Try out random_walk_demo2.py several times. Then, change the code so that the function do_walk() takes in the starting point as a parameter. Call do_walk() several times with different starting points.
We're now finally ready for the art project:
4.37 Exercise: Try out random_walk_art.py several times. Try to modify it to improve the (artistic) outcome. Describe what you did in your module pdf and submit a screenshot.
About random walks (in science):
In each of the exercises below, first try to identify the error just by reading. Then type up the program to confirm, and after that, fix the error.
4.38 Exercise: The following code wants to print the numbers from 10 to 1 in descending order:
k = 0 while k > 0: print(k) k = k - 1Identify and fix the error in my_error1.py.
4.39 Exercise: The following code wants to print the numbers from 1 through 10, along with each number's "double" (twice the number).
m = 1 n = 2 while (m <= 10) or (n <= 20): print(m, 2*n) m = m + 1 n = n + 1Identify and fix the error in my_error2.py.
4.40 Exercise: The following code wants to print the odd numbers from 1 through 9.
x = 1 while x < 10: if x % 2 == 1: # Test whether odd print(x) else: x = x + 1Identify and fix the error in my_error3.py.
4.41 Audio: