Tests, Loops, and Escapes


Topics

  • if tests
  • and, or, and not logic
  • for loops
  • Using range() in a loop
  • Nested for loops for complex data structures
  • Indexing loops with enumerate()
  • Loops with zip()
  • While loops
  • Loop control and escapes with continue and break

Introduction


In the last few action-packed sessions, we've learned quite a bit. However, we're still missing two of the most essential components of programming: logic and repetition. To add these to your Python programs, there are three key words that you will need: if, for, and while.

The objective, when you begin programming, is to make the computer do your work for you. Instead of normalizing that microarray data or computing that crystal structure fit with a pocket calculator and a bucket of coffee, you want to be able to say, 'Computer, do my work,' and then have it done. Of course, you'd have to say it a little more clearly than that -- for instance, to pick out over-expressed membrane proteins from microarray data, you could tell the computer that for each gene in the genome:

"If the protein is located in the membrane, tell me if that protein's expression level under a given set of experimental conditions [stored in column 2, for example] is 5-fold or more greater than the expression level under control conditions [stored in column 4, for example]."

Q: What's the magic word there?
A: if

Indeed, the if statement is one of the most fundamental components of programming, and it is found in some form or another in every general-purpose programming language (and nearly all specialized ones, too). It simply tells the computer to execute a block of code if a given statement is true. If the statement is false, the code is skipped. Although the concept is simple, conditional statements like if allow your programs to make decisions on their own.

Now, how can we ask the above question about every protein in our dataset of 5, or 20, or 10,000 proteins? The really great thing about computers is their ability to do things over and over again really quickly, without getting bored. It's easy for us to do stuff on a handful of data points, but more than a couple dozen (or two, depending on how much patience we have) and we'd really start wishing the computer was doing it for us. Every programming language has a way to do this, looping over a section of code multiple times. Python has two kinds of loops: the for loop and the while loop.

In principle, anything you can do with one you should be able to do with the other somehow, but it's often a lot cleaner to pick the appropriate one for the situation. Hopefully once you've seen how each one works, it will be reasonably obvious which one is best for any given job at hand.

The Syntax of if


In Python, we construct our if statements with this general form:

if <insert something that may or may not be true here>:
  • execute this line of code
  • execute this line of code too

The key pieces of syntax here are the if, the condition that we would like to evaluate (note: the condition is expressed within brackets <> for emphasis in this example, but brackets are not used in real code), the colon : that signifies the end of the logical test, and the tab-indentation for the things to do below the first line of the statement.

We will discuss what makes things True in a minute, but for now we should focus on the syntax. Beyond the if and the colon, Python will execute all of the following indented code. Indentation is the signal to the interpreter that this block of code is under the control of the if statement.

Speaking of decision making, we could use this syntax to write a program that spares us the trouble of deciding what to eat each week:

#!/usr/bin/env python
 
checkingAccountBalance = 4
 
# Let's imagine that I have to eat crappy ramen if I
# have less than $10 at the start of the week, but I
# can eat the fancier kind if I have more...
 
if checkingAccountBalance < 10:
    thisWeeksFood = 'ramen'
if checkingAccountBalance >= 10:
    thisWeeksFood = 'goodRamen'
 
print "We are eating %s this week" % (thisWeeksFood)
 
# But maybe if I have exactly $15, I can get the
# very best ramen.
 
if checkingAccountBalance == 15:
    thisWeeksFood = 'that particular brand of good ramen'
 
if checkingAccountBalance != 15:
    thisWeeksFood = 'certainly _not_ that brand!'
 # if statements can contain more than one command
    print 'absolutely not!'
 

Not bad for a start, but we'd like to clean that up a bit. Not only is "checkingAccountBalance >= 10" a pile of unnecessary keystrokes, it could also hurt us down the road. For instance, what if inflation pushed up the price of good ramen? We'd want to change out cutoff point to twelve bucks, for instance. However, if we changed the numbers in just the first if statement, but forgot to change the second, we'd have a bug in our code. By making our program simpler we can avoid such mistakes, and we can make things simpler by using python's else statement.

if checkingAccountBalance < 10:
    thisWeeksFood = 'ramen'
else:
    thisWeeksFood = 'goodRamen'

But what if there's a third option? Or a fourth? This is taken care of by Python's elif statement (short for "else if"). elif tells Python that the indented code immediately following the elif statement should only be executed if the preceding if or elif statement(s) evaluated to false.

if checkingAccountBalance < 10:
    thisWeeksFood = 'ramen'
elif checkingAccountBalance < 100:
    thisWeeksFood = 'good ramen'
elif checkingAccountBalance < 200:
    thisWeeksFood = 'better ramen'
else:
    thisWeeksFood = 'ramen that is truly profound in its goodness'

As a rule, it's good practice to account for all possibilities in your if, elif, else statements. In the case of our $10 budget, we initially cared only if we had $10 or more, and so this might be a safe place to use the if, else construct to account for all possibilities. But what if we actually had a zero balance in our checking accounts, and thus had no money to exchange for noodles? (Don't worry, you can always steal cookies at the departmental seminar.) We've accounted for all positive values with our above logic, but we've left an implicit assumption that having $0 or less on our checking account balance still results in our eating ramen this week. The point: we often make oversights that python will not overlook. These can result in interesting, though nonsensical outcomes -- like getting ramen for nothing. The lesson is to be careful and thorough as you compose your logical statements.


external image bug.png


At least we know what's for dinner.

The Nature of truth


But what is truth, anyway? Here we have a simple statement, "less than," and it's meaning is intuitive. But truth has a formal meaning in Python, as it does in all programming languages. In short:

  • Any nonzero number, nonempty object, and the special object True are things Python believes to be True.
  • Zero numbers, empty objects, and the special objects None and False are things Python believes to be False.

To evaluate an object's identity, employ the == logical operator. == evaluates whether the value of the two operands on either side are equal or not, and if they are, the statement evaluates to True. To learn more about operators, go here.

s = 0
 
if s:
    print "s is True!"
else:
    print "s is False!"
 
if s == 0:
    print "s is True!"
else:
    print "s is False!"


and, or, (and) not


Finally, what if we want to evaluate more than one if statement at once? The Boolean operators and, or, and not can help us out. The operator and will only return True if both accompanying statements are True, while or will return True if either accompanying statement is True. not returns the inverse of the logical value of the statement given.

true_statement = 'truth itself' # non-empty strings are true
false_statement = '' # empty strings are false
 
if true_statement and false_statement:
    print "This should not print."
else:
    print "This should print."
 
if true_statement or false_statement:
    print "This should print."
 
if not false_statement:
    print "This should print."
 
if not (true_statement and false_statement):
    print "This should print."

To learn about order of operations in Python: go here

for loops


The for loop allows you to perform the same actions multiple times, but with different data each time through (some languages have an equivalent construct and call it a for each loop). We've even used this concept before when talking about lists, we just didn't explain what was going on:

#!/usr/bin/python
 
li = ['Red Leicester', 'Gruyere', 'Camembert', 'Parmesan', 'Mozarella', 'Cheddar']
 
#iterate over list
for x in li: print x

$./loops.py

Red Leicester
Gruyere
Camembert
Parmesan
Mozarella
Cheddar

The general syntax here is that for goes through each item in the list (or tuple or dictionary) after the in, stores that in the variable name before in, then executes the code after the colon. Once it's done all that code, it gets a new element from the list, and repeats.

for VARIABLE_NAME in CONTAINER:
    DO_SOMETHING
    DO_SOMETHING_ELSE
    ....
 
# is the same as
 
VARIABLE_NAME = CONTAINER[0]
DO_SOMETHING
DO_SOMETHING_ELSE
 
VARIABLE_NAME = CONTAINER[1]
DO_SOMETHING
DO_SOMETHING_ELSE
 
VARIABLE_NAME = CONTAINER[2]
DO_SOMETHING
DO_SOMETHING_ELSE
...
# and so on

Just like an if statement, if you want to do more than one thing inside the loop, you can start a new block of indented lines after the colon, and then when you're done with the code you want to run every time, go back to the original indentation:

#!/usr/bin/python
 
li = ['Red Leicester', 'Gruyere', 'Camembert', 'Parmesan', 'Mozarella', 'Cheddar']
 
#iterate over list
for x in li:
    print "Cleese: Do you have any %s?" % x
    if x == 'Camembert':
        print "Chapman: Oooh, the cat's eaten it!"
    else:
        print "Chapman: No."
print "Cleese: Well I'm sorry, but I'm going to have to shoot you."

Looping a given number of times


for x in range(4):
    print 'hello'
y = range(4)
print y
$./loops.py
hello
hello
hello
hello
[0, 1, 2, 3]

Mutating lists with loops


When we were discussing lists, we said that they were mutable. We showed that in a couple of ways, such as by using the sort() method. If we wanted to change elements in a list, here's one way we might think to do it:

li2 = [1,22,48,36,101]
print li2
for x in li2:
    x = x + 42
print li2

$./loops.py
[1, 22, 48, 36, 101]
[1, 22, 48, 36, 101]

The reason this doesn't work as you might expect is actually fairly subtle, and to explain it, we're going to have a brief informative interlude about names and references.

Brief informative interlude

When we say that something is mutable, we mean that we can change the value without destroying the original data structure. To understand what this means, let's first discuss things that are immutable, such as strings and integers. Let's say we have an integer variable (for example, x = 5) and want to change its value to 10. We can't make 5 = 10, but we can easily (and efficiently) reassign x so that x = 10. The same thing is true for a string.

#!/usr/bin/python
# mutables.py
S = 'Spam'
S[0] = 'z'

$./mutables.py
Traceback (most recent call last):
  File "./loops.py", line 4, in <module>
    S[0] = 'z'
TypeError: 'str' object does not support item assignment

So what do you do if you need to mutate this immutable string? Just like with the integer, you have to reassign the variable name to the value that you now want it to have. So to transform Spam in zpam, we could do this:

#!/usr/bin/python
# mutables.py
 
S = 'Spam'
print S
 
S = 'z' + S[1:]
print S

On the other hand, lists and dictionaries can get pretty big, and it would be really inefficient in most cases to recreate the whole thing if you only want to change part of it. As it turns out, Python does not delete the original list and recreate another list with the update value, it merely changes the updated value in place. This means that instead of having to make resource-consuming copies of our data, we can change the mutable variables directly. Lists and dictionaries are mutable; tuples and strings are not.

Let's look some short code examples to compare the mutability of strings and lists.

# Let's make a list
a = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
a[0] = 'X'
print a
 
b = a
print b
 
b[1] = 'Y'
print b, "is the value of b"
print a, "is the value of a"

$ ./mutables.py
['X', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
['X', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
['X', 'Y', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'] is the value of b
['X', 'Y', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'] is the value of a

What just happened? At this point, we're going to dive a little bit into how Python works internally. Imagine that you have a giant warehouse full of stuff, and in the front office of that warehouse is a catalog telling the names of all the stuff, and where each thing is. When you make a new variable, Python does two things: it puts that new thing into the warehouse, and it updates the catalog with a new entry.

When you say "b = a", Python is a bit lazy, and since the thing that corresponds to a already exists, it just puts a new entry into the catalog with the name b and the location of the object. In a very important sense, the thing in the catalog named b is the thing in the catalog named a (and there exists, in fact, a keyword is that will confirm this, although we won't use it at all in this course). It's also possible to have two things in the catalog that look and act the same, but are different. To visualize this, let's go here.

When a and b are mutable things (like lists, or dictionaries), then changing "a" also changes "b" because they are the same thing. If you were working with an immutable object, like a string, then the only way to "change" a would be to make a new object and then update the catalog to point to that new object.

a = 'abcdefg'
# Makes a string object in the warehouse and an entry called "a"
# in the catalog that tells you where to find that object
 
b = a   # Makes a duplicate catalog entry
 
a = 'gfedcba'
# Makes a new string object, and updates the entry labelled "a" in the catalog
 
print a
print b
# The entry "b" in the catalog still tells you where to find the original string

As a general rule, whenever you have what Python calls a "container" (like a list, or a dictionary), it doesn't actually contain the object itself, it just contains another mini-catalog that tells you where to find the relevant object. For further reading on this subject, you can look at the (quite dense and hard to parse) Python documentation on the Data model and the Execution model.

Back to scheduled programming


So, to change the elements in a list, you usually have to modify the list directly, rather than the loop variable:

li2 = [1,22,48,36,101, 42]
print li2
for x in range( len(li2) ):
    li2[x] = li2[x] + 23
print li2

Here we've used the range() function to make a list of indexes as long as the list, li2. This is admittedly clunky, and Python has a built-in alternative, enumerate().

enumerate() returns a tuple with the index of the item as well as the item itself. Because of the way Python handles assignment of tuples, you can then assign that tuple to two variables, one for the index and one for the element itself. You can use the index to access the actual item in the list, not just the copy. However, you can access the copy, too, if you want.

print li2
for xInd, x in enumerate(li2):
    print x
    li2[xInd] = li2[xInd] + 23
    print x
    print
print li2

$./loops.py
[1, 22, 48, 36, 101, 42]
1
1
 
22
22
 
48
48
 
36
36
 
101
101
 
42
42
 
[24, 45, 71, 59, 124, 65]

while loop


The rough format of a while loop is similar to a for loop: there's a colon at the end of the first line, and an indented block of code that gets run every time through. Despite this general similarity, while acts a little differently than a for loop. Instead of giving a list of items to iterate over, a while loop continues until the statement between while and the colon no longer has thruthiness (e.g. False, (), [], '', etc).

#!/usr/bin/python
 
x = 'Ni!'
while x:
    print x
    x = x[1:]
print
 
x = 5
while x > 0:
    print x
    x = x - 1
print

$./loops.py

Ni!
i!
!
 
5
4
3
2
1

Escaping loops


Occasionally, you might want to get out of a loop before the truth statement is met (with a while loop) or you've gone through every element (with a for loop). In fact, some loops are designed such that the control condition at the top of the loop is never met! You can modify the default flow of the loop using break and continue. The keyword break ends the loop right where you are, while the keyword continue goes back to the top of the loop (bringing in the next item in the list if it's a for loop).

while True:
    dividend = int(raw_input("Dividend: "))
    divisor = int(raw_input("Divisor: "))
    if divisor == 0:
        break
    print dividend/divisor

#!/usr/bin/python
 
x = 10
while x:
    x = x-1
    #use mod to check if number
    #is even?  go to next number
    if x % 2 == 0:
        continue
    #use comma to print multiple
    #things on same line
    print x,
print

$./loops.py

9 7 5 3 1

In this example, each number is checked by the if statement to see if it is even. In the event the number is even, the loop goes back to the while logical expression (i.e. that x != 0) and continues.

You can use else to check whether a for or while loop finished normally, without hitting a break. Just like an if statement, the else should be at the same level of indentation as the for or while, have a colon, and then a block of code to run.

#!/usr/bin/python
 
y = 10
x = y-1
while x > 1:
    if y % x == 0:
        print x, 'is a factor of', y
        break
    x = x-1
else:
    print y, 'is prime'

$ ./loops.py

5 is a factor of 10

Here, y is checked for each number to see if it is evenly divisible (also called a factor). If y is evenly divisible, the number is printed and the code stops. If no factor is found, the loop defers to the else statement. What happens when you change the y to 100? 43? 3?

List Comprehensions


Sometimes we'll want to change every item in a list in a systematic way. For example, we may want to add one to each of a list of integers:

#!/usr/bin/python
 
a = range(10)
b = []
for i in a:
    b.append(i + 1)
a = b
print a


This is a totally acceptable way to solve this problem. However, because it is very common to commit the same operation on every member of a list, Python provides for us a shortcut that is both syntactically more concise, and computationally optimized to be much, much faster. It's called list comprehension, and it works like this:

#!/usr/bin/python
 
# let's recreate our list
a = range(10)
# note that the entire statement below is contained in **[ ]**
# This is the defining syntax of a **list comprehension**.
print [ i + 1 for i in a ]
 
# note also that we have to save the resulting new list back
# to **a** if we want to replace the old list
a = [ i + 1 for i in a ]
print a




Exercises


1. Truth or Consequences


Use the following format to evaluate the truth of some expressions in python:

query = <fill in blank>
 
if query:
    print 'Query is true'
else:
    print 'Query is false'

Try the following things in the blank. Are they True? If you don't see exactly why they are True or False, please ask a TA.

A) "" (the empty string)

B) []

C) {}

D) (a list containing an empty list)

E) 0

F) [0]

G) [0][0] (also-- what is this? Print out the value of [0][0] and [1][0] before doing the truth test)

H) [0]

2. My Pet Gene Finder


Write your first gene-finding program: have the user enter a string of six nucleotides. If the length of the string is not six, tell the user to input another string. If it is, and the six nucleotide sequence contains 'ATG,' print the position where it is found. If 'ATG' appears multiple times, print only the location of the first instance. If the string does not contain 'ATG', print -1.

Hint: one way to do this is to create the four three-base substrings of this string and then determine whether any of the resulting substrings are 'ATG.'

3. Baby's First FASTA Parser


Write your first FASTA parser: from a series of user inputs, you wish to create a dictionary keyed by gene IDs with values corresponding to their sequence. A FASTA file has the following format:
>gene1
ATAGCAGTTAGC
TTAGCAGCAGTT
ATAGCGCA
>gene2
ATGACGACGATT
TTGACGACTAGG
ACAGCC
>gene3
AGATGCCCCCTT
...

While you will learn about handling files soon, for now, assume that this file is being typed in by the user -- their first input will be '>gene1', their second will be 'ATAGCAGTTAGC', and so on. Ask the user for input 6 times (3 genes and 3 corresponding sequences). For each line input, you will have to determine whether it is a gene name or a sequence and act accordingly. Assume that the gene names and sequences will be entered in pairs (i.e. if the first input was a sequence, then the next will be the corresponding gene name, etc). Your dictionary should have keys 'gene1', 'gene2', and 'gene3' (note the absence of the '>' symbol) and the sequences of those genes as values.

Hint: Construct two lists with matching indices and combine them into a dictionary. You can use a for loop and zip() (shown below) to create a dictionary keyed by items from list a with values from list b.

# list of 0 to 10
a = range(10)
 
# list of 10 to 20
b = range(20)[10:]
 
d = dict(zip(a,b))
 
print d
 
 

4. Operator, Give Me the Number for 911


Replace "X" in this code with a statement that ensures that if the user entered "1", a "0" is printed, and if they entered a "0", a "1" is printed. Don't worry about other numbers.

s = raw_input("Enter a number: ")
s = int(s)
t = X
print t

5. FizzBuzz

FizzBuzz is a counting game where you need to be a bit on your toes. You start counting upwards, but:
When you have a number that's a multiple of three, you say "Fizz"
When you have a number that's a multiple of five, you say "Buzz"
When you have a number that's a multiple of both three and five, you say "FizzBuzz"
Otherwise, you just say the number

Write a program that plays FizzBuzz for the numbers from 1 to 100. The output should look like this:
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
...

You may find the % (modulo) operator helpful. This gives you the remainder when you divide one number by another, so:
1 % 3 gives 1
2 % 3 gives 2
3 % 3 gives 0
4 % 3 gives 1
5 % 3 gives 2
6 % 3 gives 0
7 % 3 gives 1 again, and so on.

6. Getting comfortable with Loops (adapted from Learning Python)


a) Computers only really understand numbers, not letters. Therefore, letters and symbols are stored in the computer according to numbers. The American Standard Code for Information Interchange (ASCII) is a formalized code that links characters and numbers. You determine the ASCII number code of a letter using the built-in function ord(character).
For example,
>>> print ord('T')
84
Write a for loop that prints the ASCII code of each character in the string 'Rock and Roll'. HINTS: You can loop over a string the same way you loop over a list.
b) Next, change your loop to compute the sum of the ASCII codes of all characters in the string.
c) Modify your code to print a new list that contains the ASCII codes of the characters in the string.


7. All Roads Lead to Rome (adapted from Learning Python)



A coworker (who obviously is not a native Python speaker) hands you the following code:

#!/usr/bin/python
 
L = [1,2,4,8,16,32,64]
x = 5
 
found = i = 0
while not found and i < len(L):
    #check if 2 to the power
    #of x is in the list
    if 2 ** x == L[i]:
        found = 1
    else:
        i = i+1
if found:
    print 'at index', i
else:
    print x, 'not found'

$ ./powers.py
at index 5

As is, the script does not follow normal Python coding techniques. Follow the steps below to improve it.
a) Rewrite this code with a while/else loop to eliminate the found flag and the final if statement.
b) Rewrite the example to use a for/else loop to eliminate the explicit list indexing logic.
c) Remove the loop completely by rewriting the examples using an expression with in (HINT: try the line 'print 2 in [1,2,3]')
d) Use a for loop and the list append method to generate the list L instead of typing it by hand.
e) Use a list comprehension and the exponentiation operator to generate the list L in one line (10**3 is 1000).


Solutions


1. Truth or Consequences


# "" is False
 
# [] is False
 
# {} is False
 
# [ [] ] is True, because the list isn't empty, it has a list in it!
 
# 0 is False
 
# [0] is True, because the list isn't empty
 
# [0][0] is False, because [0] is a list, and [0][0] is the first position of the list, a.k.a., 0
 
# [ [] ][0] is False, because the 0th element of the list is an empty list.

2. My Pet Gene Finder

hexamer = raw_input("Please enter a six-nucleotide sequence in all capital letters:  ")
if len(hexamer) != 6:
    print "Oh, hrmmmm... I'm fairly certain that sequence isn't of length 6."
    hexamer = raw_input("Please enter a six-nucleotide sequence in all capital letters:  ")
 
start = 0
stop = 3
 
if hexamer[start:stop] == 'ATG':
    print start, "is the position of the first Met codon."
else:
    start += 1
    stop += 1
 
if hexamer[start:stop] == 'ATG':
    print start, "is the position of the first Met codon."
else:
    start += 1
    stop += 1
 
if hexamer[start:stop] == 'ATG':
    print start, "is the position of the first Met codon."
else:
    start += 1
    stop += 1
if hexamer[start:stop] == 'ATG':
    print start, "is the position of the first Met codon."
else:
    print "-1"

3. Baby''s First FASTA Parser

# a dict for genes
 
genes = []
sequences = []
 
input1 = raw_input("Enter the gene name (format: '>genename') or sequence (all caps!):  ")
 
input2 = raw_input("If you entered the gene name above, enter the sequence here (and vice versa):  ")
 
if input1[0] == ">":
    genes.append(input1[1:])
    if input2[0] == ">":
        print "You did not enter a sequence."
    else:
        sequences.append(input2)
elif input2[0] == ">":
    genes.append(input2[1:])
    sequences.append(input1)
else:
    print "You did not enter a gene name."
 
input1 = raw_input("Enter the gene name (format: '>genename') or sequence (all caps!):  ")
 
input2 = raw_input("If you entered the gene name above, enter the sequence here (and vice versa):  ")
 
if input1[0] == ">":
    genes.append(input1[1:])
    if input2[0] == ">":
        print "You did not enter a sequence."
    else:
        sequences.append(input2)
elif input2[0] == ">":
    genes.append(input2[1:])
    sequences.append(input1)
else:
    print "You did not enter a gene name."
 
input1 = raw_input("Enter the gene name (format: '>genename') or sequence (all caps!):  ")
 
input2 = raw_input("If you entered the gene name above, enter the sequence here (and vice versa):  ")
 
if input1[0] == ">":
    genes.append(input1[1:])
    if input2[0] == ">":
        print "You did not enter a sequence."
    else:
        sequences.append(input2)
elif input2[0] == ">":
    genes.append(input2[1:])
    sequences.append(input1)
else:
    print "You did not enter a gene name."
 
answer = dict(zip(genes, sequences))
print answer

4. Operator, Give Me the Number for 911

s = raw_input("Enter a number: ")
s = int(s)
t = int(not s)
print t

5. FizzBuzz

nums = [i+1 for i in range(100)] # make a list that contains the numbers 1 - 100
 
for x in nums:
    if (x % 3 == 0) and (x % 5 == 0):
        print "FizzBuzz"
    elif x % 3 == 0:
        print "Fizz"
    elif x % 5 == 0:
        print "Buzz"
    else:
        print x

6. Getting comfortable with Loops (adapted from Learning Python)

#!/usr/bin/env python
 
rockString = 'Rock and Roll'
print rockString
print
 
# a)
print 'a)'
for x in rockString: print 'The ASCII value of %s is %s' % (x, ord(x))
print
 
# b)
print 'b)'
sum = 0
for x in rockString: sum = ord(x) + sum
print sum, '''is the sum of the ASCII values corresponding to each of the letters of the string 'Rock and Roll'.'''
print
 
# c)
print 'c)'
rockASCIIlist = [ ]
for xInd, x in enumerate(rockString):
    rockASCIIlist.append(ord(x))
    #print rockASCIIlist # uncomment this to see the list grow with each iteration
print rockASCIIlist
print

7. All Roads Lead to Rome (adapted from Learning Python)

#!/usr/bin/env python
 
# a)
print 'a)'
L = [1,2,4,8,16,32,64]
x = 5
 
# Less convoluted, but still indexing
i = 0
while i < len(L):
    if 2 ** x == L[i]:
        print 'at index', i
        break
    else:
        i = i+1
else:
    print x, 'not found'
print
 
# b)
print 'b)'
L = [1,2,4,8,16,32,64]
x = 5
for yInd, y in enumerate(L):
    if 2 ** x == y:
        print 'at index %s' % (yInd)
        break
print
 
# c)
print 'c)'
# Quickest way to see if something is in a list
print 2 in [1,2,3]
L = [1,2,4,8,16,32,64]
x = 5
print 2**x in L
print
 
# d)
print 'd)'
L = []
for x in range(7):
    L.append(2 ** x)
print L
print
 
#e)
print 'e)'
print [2**x for x in range(7)]