1. Computer Password Checker¶
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.
- Create a password to be checked
- Ask the user for their password guess and keep asking the user for their password until they guess the correct one
- 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.
The password¶
password = "12345"
Keep asking until they get the right password¶
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).
# prompt the user
prompt = "Please enter your password: "
suppliedPassword = input(prompt)
# check the password
if password != suppliedPassword:
print("Please re-execute this cell and enter in the correct password.")
else:
print("You have logged in successfully!")
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.
password = "12345"
# prompt the user
prompt = "Please enter your password: "
suppliedPassword = input(prompt)
# Keep asking the user until password is correct
# continuation condition: password != suppliedPassword
# 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
while password != suppliedPassword: # continuation condition
print("Your password is incorrect. Please re-enter the correct password.")
suppliedPassword = input(prompt)
# we can only have arrived her is password == suppliedPassword
print("You have logged in successfully!")
3. Other examples of while
loops¶
A while
loop for counting up¶
i = 0
while i < 10:
print(i)
i += 1 # this is a shorthand for i = i + 1
Another example of a while
loop that depends on user input¶
prompt = 'Please enter your name (type quit to exit): '
name = input(prompt)
while (name.lower() != 'quit'):
print('Hi,', name)
name = input(prompt)
print('Goodbye')
Example of a while
loop that depends on argument value¶
def printHalves(n):
while n > 0:
print(n)
n = n//2
printHalves(22)
printHalves(100)
printHalves(75)
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.
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.
printHalves2(22)
5. 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:
- Define an accumulation variable with an intial value (e.g., 0, the empty string).
- Iterate to accumulate a result in the accumulation variable.
- Return the final result stored in the accumulation variable
Here is a sumHalves
function that takes an nonnegative integer and returns the sum of the values printed by printHalves
.
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)
6. Iteration tables¶
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:
- Add one
print
statement right before thewhile
loop. This statement is executed once, before the loop execution begins. - Add a second
print
statement as the last statement in the loop body. This statement is executed for each iteration of the loop.
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)
Does the sumHalves2
function below have the same input/output behavior as sumHalves
? Create an iteration table that predicts the output of sumHalves2(22)
, and test your prediction below:
def sumHalves2(n):
'''Prints positive successive halves of n'''
sumSoFar = 0
while n > 0:
n = n//2
sumSoFar = sumSoFar + n
return sumSoFar
sumHalves2(22)
Here is an iteration table for sumHalves2(22)
:
n | sumSoFar |
---|---|
22 | 0 |
11 | 11 |
5 | 16 |
2 | 18 |
1 | 19 |
0 | 19 |
Moral of the story: the order of updates to state variables can affect how the loop behaves!
7. Exercise: sumDown
¶
Define a function sumDown
that takes a single argument -- an integer n
--- and returns the sum of all the integers from n
down to (and including) 1
. E.g.,
sumDown(3)
should return 6, because3 + 2 + 1 = 6
sumDown(5)
should return 15, because5 + 4 + 3 + 2 + 1 = 15
sumDown(0)
should return 0, because there are no integers from 0 to 1, and the sum of zero integers is 0.
def sumDown(n):
"""Assume n is an integer. Return the sum of the integers from n down to (and including) 1."""
#Your code here
sumSoFar = 0
while n > 0:
sumSoFar = sumSoFar + n
n = n - 1
return sumSoFar
def testSumDown(n):
"""Helper function to test countChar"""
print("sumDown(" + str(n) + ") =>", sumDown(n))
testSumDown(3)
testSumDown(5)
testSumDown(10)
testSumDown(0)
testSumDown(-1)
8. Gotcha: Premature return
from a Loop¶
The level of the indentation of a return
statement matters tremendously. In the following function, the return
statement is indented so that it is a statement in the while
loop body rather than being a statement in the function body. Predict how this difference in indentation affects the behavior of the function:
def sumHalvesBroken(n):
'''Broken version of sumHalves'''
sumSoFar = 0
while n > 0:
sumSoFar += n # or sumSoFar += n
n = n//2
return sumSoFar # wrong indentation!
# exits function after first
# loop iteration. Sometimes we
# want this, but not here!
sumHalvesBroken(22)
If a return
is enountered within the body of a loop that's within a function. it causes the function to return immediately, effectively terminating the loop early. In sumHalvesBroken
, this so-called early return causes the function to return at the end of the first iteration of the loop, just after sumSoFar
has been updated to be the value n
. So sumHalvesBroken
always just returns its argument, which is not the desired behavior. In a few lectures, we'll see examples where early returns are desirable, but in sumHalvesBroken
it does the wrong thing!
9. Another Motivating Example: counting characters¶
Assume we are given different words, such as 'Boston'
, 'Wellesley'
, 'abracadbra'
, 'osteoarthritis'
, and so on, and want to count the number how many times a particular letter occurs in the word. E.g. "Boston"
contains two 'o'
s, one 's'
, and zero 'e'
s.
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
word = "Boston"
word[0]
len(word)
word[1]
word[3]
Question: Will the following expression work?
word[6]
Question: What about this one, will this work?
word[-1]
Question: Can you access the character "s" by using a negative index? Write it below to test:
word[-4]
Sequential vs. Chained conditionals¶
How can we count the occurrences of a particular character in a word? We can go through all the indices of letters in the word and use a counter variable keep track of how many letters are equal to the character we're looking for.
But, how do we write the conditionals to test for each character?
Scenario 1: A series of if
statements¶
Can you predict the result?
char = 'o'
word = 'Boston'
charCount = 0
if word[0] == char:
charCount += 1
if word[1] == char:
charCount += 1
if word[2] == char:
charCount += 1
if word[3] == char:
charCount += 1
if word[4] == char:
charCount += 1
if word[5] == char:
charCount += 1
print("The number of times '" + char + "' appears in '" + word + "' is", charCount)
Scenario 2: Chained conditionals¶
Can you predict the result?
char = 'o'
word = 'Boston'
charCount = 0
if word[0] == char:
charCount += 1
elif word[1] == char:
charCount += 1
elif word[2] == char:
charCount += 1
elif word[3] == char:
charCount += 1
elif word[4] == char:
charCount += 1
elif word[5] == char:
charCount += 1
print("The number of times '" + char + "' appears in '" + word + "' is", charCount)
Try the code with other words¶
We always strive to write code that works in all cases. Will the following examples print the correct result? Why or why not?
char = 'e'
word = 'Wellesley'
charCount = 0
if word[0] == char:
charCount += 1
if word[1] == char:
charCount += 1
if word[2] == char:
charCount += 1
if word[3] == char:
charCount += 1
if word[4] == char:
charCount += 1
if word[5] == char:
charCount += 1
print("The number of times '" + char + "' appears in '" + word + "' is", charCount)
char = 'n'
word = 'Lynn'
charCount = 0
if word[0] == char:
charCount += 1
if word[1] == char:
charCount += 1
if word[2] == char:
charCount += 1
if word[3] == char:
charCount += 1
if word[4] == char:
charCount += 1
if word[5] == char:
charCount += 1
print("The number of times '" + char + "' appears in '" + word + "' is", charCount)
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.
char = 'o'
word = 'Boston'
charCount = 0
index = 0
while index < len(word): # continuation condition
if word[index] == char:
charCount += 1
index += 1
print("The number of times '" + char + "' appears in '" + word + "' is", charCount)
char = 'e'
word = 'Wellesley'
charCount = 0
index = 0
while index < len(word):
if word[index] == char:
charCount += 1
index += 1
print("The number of times '" + char + "' appears in '" + word + "' is", charCount)
Enapsulating the while loop in a function¶
We can now encapsulate the working while
loop in a countChar
function that takes two arguments -- a character and a word -- and returns the number of times that character appears in the word.
def countChar(char, word):
charCount = 0 # initialize accumulator variable
index = 0 # initialize index variable
while index < len(word):
if word[index] == char:
charCount += 1 # charCount only when current character is char
index += 1 # always increment index
return charCount
def testCountChar(char, word):
"""Helper function to test countChar"""
print("countAllVowels('" + char + "', '" + word + "')", "=>", countChar(char, word))
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')
10. Exercise: countAlpha
¶
A character is alphabetic if it is a lowercase or uppercase version of one of the 26 letters of the English alphabet --- i.e., a letter between a
and z
.
The .isalpha()
method on a string returns True
if all the characters in the string are alphabetic, and False
otherwise:
'Cat'.isalpha()
'C4t'.isalpha()
Applying .isalpha()
to a single character indicates if that character is alphabetic:
'Z'.isalpha()
'h'.isalpha()
'3'.isalpha()
','.isalpha()
' '.isalpha()
Using .isalpha
, define a function countAlpha
that takes a single argument -- a string word
--- and returns the number of alphabetic characters in the word. For example:
countAlpha('Wellesley')
=> 9countAlpha('CS 111 rocks!')
=> 7countAlpha('1.23 < 4.5**6')
=> 0countAlpha('You say, 'Goodbye!' & I say, 'Hello!'')
=> 22
def countAlpha(word):
"""Return the number of alphabetic characters in the string word."""
#Your code here
alphaCount = 0
index = 0
while index < len(word):
if word[index].isalpha():
alphaCount += 1
index += 1
return alphaCount
def testCountAlpha(word):
"""Helper function to test countAlpha"""
print("countAlpha('" + word + "') =>", countAlpha(word))
testCountAlpha("Wellesley")
testCountAlpha("CS 111 rocks!")
testCountAlpha("1.23 < 4.5**6")
testCountAlpha("You say, 'Goodbye!' & I say, 'Hello!'")