Thursday, March 8, 2012

Lesson 6 - Loops

Loops


Last time we learned about input and a little about if statements.  Today, let's talk about loops.  A loop is a block of code that runs a certain number of times (possibly not at all) depending on a condition or some other mechanism.

Before we get started, create a new folder in the python directory we set up in Lesson 3, and call this new folder "Lesson 6"; then open up your text editor and create a new file ("lesson6.py").

[pic]

The while Loop


The while statement begins a loop.  It looks a whole lot like an if statement, but it functions quite differently.  The loop only starts if the beginning condition is met, and the block runs to the end.  Once the loop reaches the end of its block, the condition is tested again.  If the condition is met again, the loop repeats and continues to do so until the condition is False.

How is this useful?  Well, let's think back to the previous lesson and the name entry we did.  Let's do that again, only this time, let's make it so you cannot enter an empty string for a name.  The program will keep asking until you enter a non-empty string.

Let's begin.  Type this in your document:

print "What is your name?"
name = raw_input("Enter your name>")



This will get the name from the user.  Now let's write our while loop to make sure the name is not empty.

while(name==""):
  print "You cannot enter an empty name"
  name = raw_input("Enter your name>")  


Now let's try running what we've got so far.  Open up your console and navigate to your "Lesson 6" folder.  Now run your program

python lesson6.py



Ok we've got our prompt.  Now hit return or Enter a few times.



See?  It keeps running that code over and over, and will run it forever until the condition is False.  That means if we enter something, name will no longer be the empty string, and the loop will terminate.



It worked!  We exited the loop, and the program terminated.

Now let's try something even more useful.  Say you want the user to enter an integer, but you want to be sure that they enter a number and not just letters or symbols (because if you use the int() function on a string that cannot be turned into an integer the program will crash).

Type this into your program

print
print "Now enter an integer."
strNum = raw_input("integer>")



Ok, now how do we test if strNum is an integer?  Turns out there's a string method called isdigit() that will get us pretty close to what we want.  It's not perfect, but it will do for purposes of this exercise.  Type this into your program

while(not strNum.isdigit()):
  print "You must enter a number"
  strNum = raw_input("integer>")



Wait!  What's that not doing there?  If we put not put it before a boolean, we get the opposite of the value (True instead of False, and vice versa).  In this case, we only want to run the loop if strNum is not an integer.

Now after all that, type this

num = int(strNum)



This should work for most integers (if not all).  Now let's get some output going.


print "Ok, " + name 
print "The square of your number is " + str(num * num)




That will the print the name the user entered, plus the square of the number entered.

Another useful thing you can do with loops is do operations for sequences of numbers.  For this example, let's create a new document; save it as a different name (like "lesson6-2.py").

[pic]

Ok, let's start by creating a variable to hold our number.  A common programming standard for situations like this is to use a variable called i.

i = 0

This is going to be our counter.  Let's say we want to add up all the numbers from 1 to 100.  Let's create another variable to hold the sum.

sum = 0

Ok now we need our loop.  The condition for the loop is that i <= 100, so let's start there.  Then we'll add a the value of i to sum.

while(i <= 100):
  sum = sum + i
print sum



Remember that since "print sum" is not indented that it will run after the loop finishes.  If we had indented it, it would print the value of sum each time through the loop.

Now let's run it.



Oops!  What's happening?  We didn't ask for input?  Why isn't anything happening?  That's because we forgot one tiny detail.  See the condition?  Each time through the loop, i is always 0, so we end up looping forever!  We need to change our program a bit.

But first use an interrupt to exit your program.  Hit ctrl+C.



We exited our program.   Ok so now how do we fix the loop?  Just change the loop to be this:


while(i <= 100):
  sum = sum + i
  i = i + 1




Now let's try to run it.



It worked!  Now for a few optimizations.  For instance, when i is 0, we're adding 0 to 0 (in sum).  We really don't need that (not that it matters too much as far as performance goes).  So let's just change

i = 1



That's better.  Now for the assignment with addition.  There are special assignment operators for most mathematical operations that tell Python to, for example, add a number to a variable and store the result back in that variable.  For instance, on the line

sum = sum + i

we can instead say

sum += i



And we can do the same thing for i.

i += 1



There!  Our program is much more succinct.

Now let's move on to another kind of loop.

The for Loop

In Python, the for loop is your friend.  It helps you iterate (repeat a process on) through collections like lists and so forth.  Remember back in Lesson 4 when we first learned about lists and we wrote a program that printed items in a list to the screen one by one using square brackets ([])?  That didn't seem very useful, and it's not.  The better way is to use a for loop.

Open a new file in your text editor and save it (lesson6-3.py).

First we'll create a list of strings.  Let's use names.

nameList = ["Bob", "Sue", "Mary", "Joe"]

Now let's say that we want to print each of those names to the console.  We could do something like this:

print nameList[0]
print nameList[1]
print nameList[2]
print nameList[3]

It works, but it's a lot of duplicate code, and what if we didn't know how long the list was when we were writing the program?  With the for loop, the list can be any size at all.  Here's what the for loop looks like:

for thisName in nameList:

Ok, here's what it means.  We want to create a loop which runs once for each item in nameList.  Each time through the loop, the item from the list we are using will be stored in the variable thisName.  (We know that these will all be strings.)

Now for the useful part.

  print thisName

So the total loop looks like this:


for thisName in nameList:
  print thisName



Now let's run it and see what we get.


It worked!  It went through each item in the list (in order), and we told it to print that item to the console.

There's a way we can do the same thing with a while loop.  First let's create a line.

print "-" * 80

Now let's create a variable that will hold the index of the item we are working on.

i = 0

Now we'll create our loop.

while i < len(nameList):

What does this mean?  Well, let's finish the rest of the loop, and then I'll explain.  (Remember to indent)


  print nameList[i]
  i += 1




All right.  See how in that last line we are adding 1 to i and then reassigning it?  (We could have written it "i = i + 1")  The value in i will always increase by 1 each time through the loop.  The condition of this while loop will remove us from the loop when i is not less than the length of the list.  (Since we're adding 1 each time, this will happen when i equals the length of the list (4).)

So the values being tested for i will be 0, 1, 2, 3, and 4.  When i equals 4, it is no longer less than the length of the list, and the loop doesn't run anymore.

This is what makes zero-based lists so advantageous.  The last item in the list will always be the length of the list minus 1.  Therefore, loops through a zero-based lists always check to see that the index is less than (<) the length.  If this were one-based, we would have to check to see if i was less than or equal to the length (<=) because the last item index and the length of the list would be the same.

Remembering that one thing will help you out a lot in programming.  But see how in this case the for loop is much simpler?  (There will be times when you will want to use a while loop, but we won't get into that now.)

Okay, now let's try it



Ta-da!  Now, if we really wanted to emulate the for loop, we would have done something like this:


i = 0
while i < len(nameList):
  thisName = nameList[i]
  print thisName
  i += 1

As far as our program is concerned, these two loops do the exact same thing—they both go through each item in the list and assign the item to the variable thisName.


Let's Put It All Together

Ok now, let's write a program that incorporates everything we covered in this lesson.  Create a new file and save it (lesson6-4.py).

We are going to make a program that lets the user enter in as many words as he wants.  When that's done, we'll change each word to uppercase and then print them to the screen.

First, we'll create the list to hold the words.  (Okay, they don't necessarily have to be words; they can be any string, but for purposes of this program, we'll call them words.)

wordList = []

Now let's make a variable to hold the user's input.

currentWord = ""

Let's do something to spice things up a bit.  Each time the user enters a word, let's give them a prompt that tells them what number of word they are entering (eg "word 3:").  Let's create a variable to hold that number.

wordNum = 1

Why are we starting at 1?  In user interface design, you generally don't start at 0.  Think of a spreadsheet program (like Microsoft Excel); the numbering starts at 1—not 0.  Thus we will start at one, though we know the actual index will be one less than whatever the prompt says.

Now let's start our loop.

while currentWord != "":

This will kick us out of the loop when the user enters an empty string (in other words, the user just hits return or Enter).  But we have a problem now: the first time through the loop, currentWord is already an empty string.  How can we fix this?  How about we put an input statement before the loop.  Change the line

currentWord = ""

to

currentWord = raw_input("word 1: ")

By doing that, our wordNum will be off by 1 when we start the loop, so let's change it to be

wordNum = 2



There we go.  See how thinking through things helps?  You don't have to get things right the first time; if something doesn't work, or you think of a better way, change it.

Ok now for the body of the loop.  First let's add our word to the list (remember to indent)

  wordList.append(currentWord)

We know for certain that we're not adding an empty string, because we just tested for that as the condition for the loop.  If the user hits return or Enter on the first word, the loop won't run even once.

Now let's create the string for our prompt

  prompt = "word " + str(wordNum) + ": "

I hope that's pretty self-explanatory.  (Remember that the str() function is necessary so we can convert the integer to a string before concatenation.)

Now for the input using the prompt we just made.

  currentWord = raw_input(prompt)

And last but not least, we want to increment (add one to) wordNum.

  wordNum += 1

Without that, our prompt would be "word 2: " each time through the loop.

Now that we've got our word entering loop, let's create a loop to display the uppercase words.  We can do this in one of two ways: we can change them all to uppercase, store them, and then display them; or we can change them to uppercase at the same time as we display them.

The second method is the easiest, since we don't care to have the uppercase words when we're done:

for word in wordList:
  print word.upper()

Remember that upper() (like all string functions) only returns the uppercase string; it doesn't change the original string.

Incidentally, we could have also stored each word as an uppercase string at the time the user entered it by doing this:

  wordList.append(currentWord.upper())

Which way is the best way?  That depends on what you want.  If you don't care to have the original input, convert it when the user enters it.  If you don't care to store the uppercase strings, convert it when you print it.  If you want to keep both, do what we're about to do next.

First, let's make a new list for the uppercaseStrings.

uppercaseWords = []

Now let's go through our list, convert them, and add them to the new list.

for word in wordList:
  uppercaseWords.append(word.upper())

Now to print them.

for word in uppercaseWords:
  print word

There we go.  Now let's run it.



Voila!  It works.

Review
  • Loops are blocks of code that can run more than once.
  • The while loop runs until its condition evaluates to True.
  • Code that is part of the loop is indented.
  • A counter is an integer that is incremented each time through the loop so that you can keep track of how many times the loop has run.
  • The for loop is used to loop through items in a collection (like a list, for example).
Well that's it for now.  See you next time.