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.
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.
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.
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).
# prompt the user
prompt = "Please enter your password: "
enteredPassword = input(prompt)
# check the password
if password != enteredPassword:
print("Please re-execute this cell and enter in the correct password.")
else:
print("You have logged in successfully!")
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: "
enteredPassword = input(prompt)
# Keep asking the user until password is correct
# continuation condition: password != enteredPassword
# if continuation condition is True, ask to enter a
# new password because enteredPassword is incorrect
# if continuation condition is False, do not execute
#. the loop body, because enteredPassword is correct
while password != enteredPassword: # continuation condition
print("That password is incorrect. Please enter the correct password.")
enteredPassword = input(prompt)
# we can only have arrived here if password == enteredPassword
print("You have logged in successfully!")
while
loops¶while
loop for counting up¶i = 0
while i < 10:
print(i)
i += 1 # this is a shorthand for i = i + 1
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')
while
loop that depends on argument value¶def printHalves(n):
while n > 0:
print(n)
n = n//2
printHalves(22)
printHalves(100)
printHalves(75)
We sometimes might wrongly write an infinite loop, one that never ends. In these cases, use Ctrl+C or Kernel>Interrupt 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 # misindented assignment statement causes an infinite loop!
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)
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:
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 # initialize accumulation variable
while n > 0:
sumSoFar += n # update accumulation variable
n = n//2
return sumSoFar # return final value of accumulation variable
sumHalves(22)
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:
print
statement right before the while
loop. This statement is executed once, before the loop execution begins.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!
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, because 3 + 2 + 1 = 6
sumDown(5)
should return 15, because 5 + 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)
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 iteration
# of loop. Sometimes we want to return
# early from a loop, 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!
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.
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]
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?
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)
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)
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)
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)
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')
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!'')
=> 22def 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!'")