Let's try and build a simple password checker using the tools we know. The general idea of a password checker is that the user needs to keep trying passwords until they get the correct password. At this point, the user is logged in. What are the key steps we need to build this program? Let's make an outline.

1. Create a password to be checked
3. Perform whatever action once the user has logged in

There are several key elements that we need to identify. The most important is that there will be some code that we have to repeat potentially if the user keeps guessing incorrectly. Eventually, we use loops to help us. Loops repeat code. Second, we need to keep repeating until some condition is met, namely the user has entered in their password correctly. Formally, we call this a continuation condition. It is the condition that determines whether we need to repeat or to move on to someother code.

### Manual Implementation¶

It turns out that without loops we do not have the means to implement this program. However, we can simulate what we should do by reexecuting certain code cells.

In [1]:
password = "12345"


Here, we need to continually execute the cell until we are no longer prompted to enter a password. We will have to do this manually with the tools we know. This is the moment though where we will need a loop because we want to repeat this block of code until some condition is met (i.e., the password is entered correctly).

In [2]:
# prompt the user

else:
print("You have logged in correctly!")

Please enter your password: 12345
You have logged in correctly!


This is the code that would execute once the user has guessed the correct password.

In [3]:
print("Welcome")

Welcome


## 2. Introducing while loops¶

A while loop is a control flow statement, that repeats a body of code until some condition is met. This is perfect for our password checker! The condition is called a continuation condition. If the continuation condition is True, then we will continue to execute the code body one more time. If the continuation condition is False, then we will not continue and move on to the next statement. The chart below details the control flow of a while loop outlined with the dotted dashes.

Below is our first example of a while loop to implement our password checker.

In [4]:
password = "12345"

# prompt the user

# if continuation condition is True, keep asking for password because they are not equal
# if continuation condition is False, do not execute the body and ask for age because password is correct

print("You have logged in successfully!")

Please enter your password: 13456
You have logged in successfully!


## 3. Other examples of while loops¶

### A while loop for counting up¶

In [5]:
i = 0
while i < 10:
print(i)
i += 1

0
1
2
3
4
5
6
7
8
9


### Another example of a while loop that depends on user input¶

In [6]:
prompt = 'Please enter your name (type quit to exit): '
name = input(prompt)

while (name.lower() != 'quit'):
print('Hi,', name)
name = input(prompt)

print('Goodbye')

Please enter your name (type quit to exit): Anna
Hi, Anna
Hi, Andy
Hi, Val
Hi, Simon
Goodbye


### Example of a while loop that depends on argument value¶

In [7]:
def printHalves(n):
while n > 0:
print(n)
n = n//2

printHalves(100)

100
50
25
12
6
3
1

In [8]:
printHalves(22)

22
11
5
2
1

In [9]:
printHalves(75)

75
37
18
9
4
2
1


## 4. Gotcha: Infinite Loops¶

We sometimes might wrongly write an infinite loop, one that never ends. In these cases, use Ctrl+C to break out of the loop.

In [10]:
def printHalves2(n):
"""Attempts to print positive successive halves of n.
"""
while n > 0:
print(n)
n = n//2


NOTE: In the Notebook, it might not be possible sometimes to break the loop, even with Kernel|Interrupt. In this case, close the tab and open it again.

In [ ]:
printHalves2(22)


## 5. Exercise 1: define countDown¶

Write a function called countDown that takes a number and prints all the numbers from that number down to 1. You can assume that number is an integer.

For example:

countDown(5)
5
4
3
2
1
In [11]:
# Your code here
def countDown(highNum):
while highNum > 0:
print(highNum)
highNum -= 1

In [12]:
countDown(10)

10
9
8
7
6
5
4
3
2
1

In [13]:
countDown(5)

5
4
3
2
1


## 6. Exercise 2: printOdds¶

Define a function called printOdds that takes a number and prints all the odd numbers from 1 up to and including that number (if it is odd). For example:

printOdds(10)
1
3
5
7
9
printOdds(13)
1
3
5
7
9
11
13
In [14]:
# Your code here

# Solution 1
def printOdds(x):
n = 1
while n <= x:
if n % 2 == 1:
print(n)
n += 1

# Solution 2
def printOdds(x):
n = 1
while n <= x:
print(n)
n += 2

In [15]:
printOdds(10)

1
3
5
7
9

In [16]:
printOdds(13)

1
3
5
7
9
11
13


## 7. The Accumulating Pattern¶

It is common to use a while loop in conjunction with one or more accumulators = variables that accumulate results from processing the elements of a sequence.

All accumulation patterns follow these three steps:

1. Define an accumulation variable
2. Iterate to accumulate
3. Return the accumulation variable

Here is a sumHalves function that takes an nonnegative integer and returns the sum of the values printed by printHalves.

In [17]:
def sumHalves(n):
"""Accumulates the sum of elements that are halves of the preceding numbers.
"""
sumSoFar = 0
while n > 0:
sumSoFar += n
n = n//2
return sumSoFar

sumHalves(22)

Out[17]:
41

## 8. Printing the iteration table¶

We can understand the execution of a loop by an iteration table whose columns are state variables that change during the loop and where each row indicates the values of the state variables at a particular point in time.

Here is an iteration table for sumHalves(22):

n sumSoFar
22 0
11 22
5 33
2 38
1 40
0 41

You can get a Python loop to display the rows of an iteration table by adding two print statements:

1. Add one print statement right before the while loop. This statement is executed once, before the loop execution begins.
2. Add a second print statement as the last statement in the loop body. This statement is executed for each iteration of the loop.
In [18]:
def sumHalvesTable(n):
sumSoFar = 0
print('n:', n, '| sumSoFar:', sumSoFar) # First print is executed once
while n > 0:
sumSoFar += n # or sumSoFar = sumSoFar + n
n = n//2
print('n:', n, '| sumSoFar:', sumSoFar) # Second print may be executed multiple times (including zero)
return sumSoFar

sumHalvesTable(22)

n: 22 | sumSoFar: 0
n: 11 | sumSoFar: 22
n: 5 | sumSoFar: 33
n: 2 | sumSoFar: 38
n: 1 | sumSoFar: 40
n: 0 | sumSoFar: 41

Out[18]:
41

Does the sumHalves2 function have the same input/output behavior as sumHalves? Create an iteration table that predicts the output of sumHalves2(22), and test your prediction below:

Here is an iteration table for sumHalves2(22):

n sumSoFar
22 0
11 11
5 16
2 18
1 19
0 19
In [19]:
def sumHalves2(n):
'''Prints positive successive halves of n'''
sumSoFar = 0
while n > 0:
n = n//2
sumSoFar = sumSoFar + n
return sumSoFar

sumHalves2(22)

Out[19]:
19

Moral of the story: the order of updates to state variables can affect how the loop behaves!

## 9. Exercise 3: define sumBetween¶

Define a function sumBetween that takes two integer parameters lo and hi and returns the sum of the integers between lo and hi (inclusive). For example:

sumBetween(4, 8)
30

This is because (4 + 5 + 6 + 7 + 8) is 30.

In [20]:
def sumBetween(lo, hi):
sumSoFar = 0           # accumulator varible
while lo <= hi:
sumSoFar += lo     # update state variables
lo += 1
return sumSoFar        # return accumulator

sumBetween(4, 8)

Out[20]:
30

Now modify your definition of sumBetween to print an iteration table with the values of the state variables lo, hi, and sumSoFar. It should look something like this:

sumBetweenTable(4, 8)
lo: 4 | hi: 8 | sumSoFar: 0
lo: 5 | hi: 8 | sumSoFar: 4
lo: 6 | hi: 8 | sumSoFar: 9
lo: 7 | hi: 8 | sumSoFar: 15
lo: 8 | hi: 8 | sumSoFar: 22
lo: 9 | hi: 8 | sumSoFar: 30
In [21]:
def sumBetweenTable(lo, hi):
sumSoFar = 0           # accumulator varible
print('lo:', lo, '| hi:', hi, '| sumSoFar:', sumSoFar)
while lo <= hi:
sumSoFar += lo     # update state variables
lo += 1
print('lo:', lo, '| hi:', hi, '| sumSoFar:', sumSoFar)
return sumSoFar        # return accumulator

sumBetweenTable(4, 8)

lo: 4 | hi: 8 | sumSoFar: 0
lo: 5 | hi: 8 | sumSoFar: 4
lo: 6 | hi: 8 | sumSoFar: 9
lo: 7 | hi: 8 | sumSoFar: 15
lo: 8 | hi: 8 | sumSoFar: 22
lo: 9 | hi: 8 | sumSoFar: 30

Out[21]:
30

## 10. Another Motivating Example: counting vowels¶

Assume we are given different words, such as "Wellesley", "Boston", "abracadbra", "osteoarthritis", and so on, and want to count the number of vowels in each word, or to count how many times a particular letter occurs in the word.

How do we do that? First, we'll need our "old friend", isVowel as a predicate that we can use in conditional statements.

### Old friend: isVowel¶

In [22]:
def isVowel(char):
"""Predicate that returns true only when a letter is a vowel."""
return char.lower() in "aeiou"

In [23]:
def testIsVowel(char):
"""Helper function to test isVowel"""
print("isVowel('" + char + "')",  "=>",  isVowel(char))

In [24]:
testIsVowel('d')
testIsVowel('D')
testIsVowel('a')
testIsVowel('E')
testIsVowel('i')
testIsVowel('O')
testIsVowel('u')
testIsVowel('y')

isVowel('d') => False
isVowel('D') => False
isVowel('a') => True
isVowel('E') => True
isVowel('i') => True
isVowel('O') => True
isVowel('u') => True
isVowel('y') => False


## Use indices to access elements in a string¶

We can access each character (or element) in a string word by using indices. These are integers from 0 to len(word)-1

In [25]:
word = "Boston"
word[0]

Out[25]:
'B'
In [26]:
len(word)

Out[26]:
6
In [27]:
word[1]

Out[27]:
'o'
In [28]:
word[3]

Out[28]:
't'

Question: Will the following expression work?

In [29]:
word[6]

---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-29-8fed510628cf> in <module>
----> 1 word[6]

IndexError: string index out of range

In [30]:
word[-1]

Out[30]:
'n'

Question: Can you access the character "s" by using a negative index? Write it below to test:

In [31]:
word[-4]

Out[31]:
's'

## Sequential vs. Chained conditionals¶

How can we count the vowels in a word? We can use isVowel and the indices to test each character and keep track of vowels through a counter variable.

But, how do we write the conditionals to test for each character?

### Scenario 1: A series of if statements¶

Can you predict the result?

In [32]:
word = 'Boston'
counter = 0
if isVowel(word[0]):
counter += 1
if isVowel(word[1]):
counter += 1
if isVowel(word[2]):
counter += 1
if isVowel(word[3]):
counter += 1
if isVowel(word[4]):
counter += 1
if isVowel(word[5]):
counter += 1
print(counter)

2


### Scenario 2: Chained conditionals¶

Can you predict the result?

In [33]:
word = 'Boston'
counter = 0
if isVowel(word[0]):
counter += 1
elif isVowel(word[1]):
counter += 1
elif isVowel(word[2]):
counter += 1
elif isVowel(word[3]):
counter += 1
elif isVowel(word[4]):
counter += 1
elif isVowel(word[5]):
counter += 1
print(counter)

1


### Try the code with another word¶

We always strive to write code that is generic, what will happen when we run it with a new string?

In [34]:
word = 'Lynn'
counter = 0
if isVowel(word[0]):
counter += 1
if isVowel(word[1]):
counter += 1
if isVowel(word[2]):
counter += 1
if isVowel(word[3]):
counter += 1
if isVowel(word[4]):
counter += 1
if isVowel(word[5]):
counter += 1
print(counter)

---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-34-62d91339842c> in <module>
9 if isVowel(word[3]):
10     counter += 1
---> 11 if isVowel(word[4]):
12     counter += 1
13 if isVowel(word[5]):

IndexError: string index out of range

## Using a While Loop¶

Remember a while loop is used to repeat a block of code. What code do we want to repeat? We want to repeat the conditional for each index in the string. Here we can use a while loop to start at index 0 and repeat until we reach the last index of the string. We will need two accumulation variables. One to increment the counter and one to increment the index.

In [35]:
word = "Boston"
counter = 0
index = 0

while index < len(word): # continuation condition
if isVowel(word[index]):
counter += 1
index += 1

print(word + ' has ' + str(counter) + ' vowels')

Boston has 2 vowels

In [36]:
word = "Manchester-by-the-Sea"
counter = 0
index = 0

while index < len(word):
if isVowel(word[index]):
counter += 1
index += 1

print(word + ' has ' + str(counter) + ' vowels')

Manchester-by-the-Sea has 6 vowels


## Exercise 4: countAllVowels¶

Abstract over the while loop examples from above to define a function countAllVowels that takes one argument -- a word -- and return the number of vowels in that word.

In [37]:
# Your code here
def countAllVowels(word):
counter = 0
index = 0

while index < len(word):
if isVowel(word[index]):
counter += 1
index += 1

return counter

In [38]:
def testCountAllVowels(word):
"""Helper function to test countAllVowels"""
print("countAllVowels('" + word + "')",  "=>",  countAllVowels(word))

In [39]:
testCountAllVowels('Lynn')
testCountAllVowels('Boston')
testCountAllVowels('Wellesley')
testCountAllVowels('Deerfield')
testCountAllVowels('Middleborough')
testCountAllVowels('Manchester-by-the-Sea')
testCountAllVowels('pneumonoultramicroscopicsilicovolcanoconiosis')

countAllVowels('Lynn') => 0
countAllVowels('Boston') => 2
countAllVowels('Wellesley') => 3
countAllVowels('Deerfield') => 4
countAllVowels('Middleborough') => 5
countAllVowels('Manchester-by-the-Sea') => 6
countAllVowels('pneumonoultramicroscopicsilicovolcanoconiosis') => 20


## Exercise 5: countChar¶

Define a function countChar that takes two arguments -- a character and a word -- and returns the number of times that character appears in the word.

In [40]:
# Your code here
def countChar(char, word):
counter = 0
index = 0

while index < len(word):
if word[index] == char:
counter += 1
index += 1

return counter

In [41]:
def testCountChar(char, word):
"""Helper function to test countChar"""
print("countAllVowels('" + char + "', '" + word + "')",  "=>",  countChar(char, word))

In [42]:
testCountChar('i', 'Mississippi')
testCountChar('p', 'Mississippi')
testCountChar('M', 'Mississippi')
testCountChar('m', 'Mississippi')
testCountChar('-', 'Manchester-by-the-Sea')
testCountChar('t', 'To be or not to be')
testCountChar(' ', 'To be or not to be')

countAllVowels('i', 'Mississippi') => 4
countAllVowels('p', 'Mississippi') => 2
countAllVowels('M', 'Mississippi') => 1
countAllVowels('m', 'Mississippi') => 0
countAllVowels('-', 'Manchester-by-the-Sea') => 3
countAllVowels('t', 'To be or not to be') => 2
countAllVowels(' ', 'To be or not to be') => 5