HW6: Definite Loop Practice

15 points; due Wed 4/16 @ 9am.

Goals

The goal of this assignment is to get a sense of the different ways that definite loops work in Python, and how to use them.

Setup and Requirements

This is an independent assignment. Similar to HW2 (Intro to Unix), there is not a lot to hand in; the idea is to go through some examples independently, study them, and make sure you understand them. There is a hand-in, though, which you'll build as you go through this exercise.

This assignment is to prepare you for a good discussion in class of definite loops. You read about definite loops way back at the beginning of the course, in the first reading assignment (Chapter 2). If you need a refresher, please go back and read that chapter.

Your Assignment

Introduction

The for x in y: loop behaves differently depending on what type of object y refers to. This exercise has you investigate what happens for a few different types of y.

The first step of this assignment is to create a directory called definite-loops.

The second step of this assignment is to create an empty Python file called definite-loops.py in that directory. Then open your Terminal program, navigate to the definite-loops directory, and you're ready to go.

This assignment is composed of a number of short code snippets. You should work through each section one by one, copying the snippets in that section into your definite-loops.py file. There are lots of snippets, and they print out lots of output. So each time you move onto a new section, you should replace the old code with the new code, so that you don't get distracted by the old output while you're working on the new section.

Again, this assignment is about “investigating” — don't just run the code snippets, mess around with them. Change values and print out types to get a deeper understanding of what's going on. It might be worthwhile to take notes as you go, writing down what you believe is happening.

Looping over a List

Suppose y is a list. Then what kind of thing is x during each iteration of the loop?

print '====== looping over a list ======'
y = ['emu', 'auk', 'moonlight']
for x in y:
    print x
print
print '====== looping over another list ======'
y = [3, 1, 4]
for x in y:
    print x
print
print '====== looping over a mixed list ======'
y = [3, 'auks', 4, 'emus']
for x in y:
    print x
print

Looping over a String

What if y is a string? When this loop runs, what kind of thing is x?

print '====== looping over a string ======'
y = 'emus and auks in the moonlight'
for x in y:
    print x
print

Looping over a File

Suppose y is an open File object. What is x then? For this example, create a file in the same directory as your code,

print '====== looping over a file ======'
y = open('definite-loops.py', 'r')
for x in y:
    print x
print
# Remember you have to close a file after you're done with it!
y.close()

Looping over an Integer

Suppose y is an integer. What is x then?

print '====== looping over an integer ======'
y = 15
for x in y:
    print x
print

Looping over a Range

You might not be able to loop over an integer (d'oh! I gave it away!), but you can loop over a “range” of integers.

print '====== looping over a range ======'
m = 7
for x in range(m):
    print x
print

What's the relationship between m and the last value that x takes? Where have we seen similar behavior before?

And why does looping over a range work? Let's check:

print range(7)
print type(range(7))

So what kind of object is range(7)? (That is, what kind of object does the expression range(7) evaluate to?)

More Looping with Ranges

Ranges are pretty flexible:

print '====== looping over other types of range ======'
for x in range(3,8):
    print x
print
for x in range(3, 27, 5):
    print x
print

What can you tell about the arguments to range()? Have we seen something similar before?

Looping through a List using range()

Another way of looping through a list (or string) is to use range() to loop over the valid indices of that list, and to access the list at that index.

print '====== looping through a list by index ======'
L = ['spam', 'eggs', 'sausage']
for i in range(len(L)):
    print L[i]
print

Why might you want to do this? Well, the index is just a number, and you can do other computations with it.

L = ['spam', 'eggs', 'sausage']
for i in range(len(L)-1):
    print L[i], L[i+1]
print

What just happened here? Why did we need to do range(len(L)-1)?

The Body of a Loop

The “body” of a loop is a set of Python statements that you want to execute on each pass through the loop. In most of the examples above, the body has been a simple print statement. But the body can be any sequence of Python commands at all! It's very similar, in that way, to the body of a function.

total_length = 0
menu = ['spam', 'eggs', 'sausage']
for dish in menu:
    print dish
    total_length = total_length + len(dish)
    print "The total length computed so far is", total_length
print "The grand total of the lengths of the dishes is", total_length

Notice that we use indentation to indicate the body of a loop.

Nested Loops

The body of a loop can be any Python code! This includes loops, in fact. Putting loops inside of other loops is a very common thing in programming. Before you run this code snippet, try to predict what it will print out. Then check and see if you were right.

print '====== nested loops, first example  ======'
for j in range(4):
    for k in range(3):
        print j, k
print

Nested loops can be pretty confusing, so study this one carefully. What exactly is going on?

Another Nested Loop

Here's a trickier nested loop. Same as before: first try to predict what this snippet will print out, before you actually run it.

print '====== nested loops, trickier ======'
for j in range(4):
    for k in range(j):
        print j, k
print

Your Assignment, Part 2

The mathematical constant $\pi$ is, as you hopefully know, the ratio of the circumference of a circle to its diameter. $\pi$ is an irrational number, meaning its decimal representation goes on forever and never repeats. So computer scientists always have something to do if they're bored: compute a few more digits of $\pi$! There are dozens (if not hundreds) of ways to compute $\pi$, and many of them are based on the idea of an approximating series: a mathematical formula that has a pattern that can go on forever. The longer you follow the pattern, the more digits of $\pi$ you compute correctly, and the better your approximation becomes.

Here's an extremely simple approximation of $\pi$: $$\pi = \frac{4}{1} - \frac{4}{3} + \frac{4}{5} - \frac{4}{7} + \frac{4}{9} - \frac{4}{11} + \frac{4}{13} \ldots$$ The pattern should be clear: we alternately add and subtract fractions whose numerator is 4 and whose denominator is an odd number, increasing the denominator each time. If we actually continued this pattern forever, the value we would compute is exactly $\pi$. However, if we stop the pattern at some finite number of terms, we get an approximation of $\pi$.

Create a new Python file called pi-approx.py. In it, use definite loops and math operators to compute an approximation of $\pi$ using this series. Get your program to compute it using the first 50000 terms of the series (that is, $\frac{4}{1}$ all the way up to $\frac{4}{99999}$). Add a comment at the end of your file to say how many of the first digits of $\pi$ it got correct before starting to get them wrong.

I'll get you started:

# Pi approximation, by __YOUR_NAME_HERE__
pi_approx = 0.0
num_terms = 7  # At first, experiment with 7 terms, then bump this up to 50000

# Now do some stuff to compute pi...

print "After", num_terms, "terms, my approximation of pi is", pi_approx

# For num_terms = 50000, this gets the first _____ digits of pi correct.

Submission and Grading

Submit your file pi-approx.py on Moodle.