6. Control Flow: Loops#

Last class we covered conditional statements, today we’ll introduce loops.

  • ~~Conditional Statements - statements that allow you to run different code under certain conditions~~

  • Loops - statements that allow you to run blocks of code multiple times

6.1. Loops#

Loops allow you to execute a block of code repeatedly. This is useful if you need to perform the same set of actions on several (tens or maybe hundreds) of different items in some container (e.g. loading files from a list of filenames, parsing text into sentences or clauses, extracting specific data that meet some criteria from a larger set). Once you understand loops, you’ll find they’re incredibly useful for automating tedious analyses. BUT for large data and complex operations, loops can get very slow; so for many data science use cases, we try to find ways to avoid using loops.

The two main kinds of loops, common to most programming languages are:

  • while loops - perform a block of code as long as some condition holds true

  • for loops - perform a block of code a set number of times

Later, we’ll learn list and dictionary comprehensions (one of the most fun and useful features of Python).

6.1.1. While loops#

Run some code repeatedly as long as a condition is met:

  • I’m going to work on homework problems until 10p.

  • It’s an all-you-can-eat buffet, I’m going to eat as long as there’s room in my stomach.

  • Keep driving straight until you see a Wendy’s on the left, then turn left.

6.1.2. For loops#

Run some code repeatedly some set amount of times:

  • I’m going to work through the first four homework problems.

  • I’m going to eat one forkful of each dish at the buffet.

  • Keep driving straight past six traffic lights, then turn left.

We’ll start with while loops:

6.1.3. While loops#

6.1.3.1. Syntax for While loops:#

while <condition>:
    <code block>

6.1.3.2. Example Problem 1#

Suppose we are developing a mobile game. Our game permits in-app purchases, as such, users must “verify” they are 18+ years old. We’ll keep asking until we get the answer we want.

Use if statements to check if the value entered is a number and if that number is greater than or equal to 18.

Tips:

  • input(prompt) asks the user to enter a value in a text field.

  • .isnumeric() checks if the content of a string is numeric.

x = input('Type your name:')
---------------------------------------------------------------------------
StdinNotImplementedError                  Traceback (most recent call last)
Cell In[1], line 1
----> 1 x = input('Type your name:')

File ~/.pyenv/versions/3.13.1/envs/datascience/lib/python3.13/site-packages/ipykernel/kernelbase.py:1281, in Kernel.raw_input(self, prompt)
   1279 if not self._allow_stdin:
   1280     msg = "raw_input was called, but this frontend does not support input requests."
-> 1281     raise StdinNotImplementedError(msg)
   1282 return self._input_request(
   1283     str(prompt),
   1284     self._parent_ident["shell"],
   1285     self.get_parent("shell"),
   1286     password=False,
   1287 )

StdinNotImplementedError: raw_input was called, but this frontend does not support input requests.
print(type(x))
print(x.isnumeric())
# print(type(int(x))) # this will return an error if your name is not numeric (probably isn't)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[2], line 1
----> 1 print(type(x))
      2 print(x.isnumeric())
      3 # print(type(int(x))) # this will return an error if your name is not numeric (probably isn't)

NameError: name 'x' is not defined
joke = input('Tell me a joke:')
---------------------------------------------------------------------------
StdinNotImplementedError                  Traceback (most recent call last)
Cell In[3], line 1
----> 1 joke = input('Tell me a joke:')

File ~/.pyenv/versions/3.13.1/envs/datascience/lib/python3.13/site-packages/ipykernel/kernelbase.py:1281, in Kernel.raw_input(self, prompt)
   1279 if not self._allow_stdin:
   1280     msg = "raw_input was called, but this frontend does not support input requests."
-> 1281     raise StdinNotImplementedError(msg)
   1282 return self._input_request(
   1283     str(prompt),
   1284     self._parent_ident["shell"],
   1285     self.get_parent("shell"),
   1286     password=False,
   1287 )

StdinNotImplementedError: raw_input was called, but this frontend does not support input requests.
joke
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[4], line 1
----> 1 joke

NameError: name 'joke' is not defined
def verifyAge():
    x = input('Enter your age:')
    while not x.isnumeric() or int(x) < 18:
        if not x.isnumeric():
            x = input('Please enter a NUMERIC value for age:')
        elif int(x)<18:
            x = input('Are you sure? You seem older than that. Please enter your "ACTUAL" age:')
    print('Congratulations! You are verified.')
           
verifyAge()
---------------------------------------------------------------------------
StdinNotImplementedError                  Traceback (most recent call last)
Cell In[6], line 1
----> 1 verifyAge()

Cell In[5], line 2, in verifyAge()
      1 def verifyAge():
----> 2     x = input('Enter your age:')
      3     while not x.isnumeric() or int(x) < 18:
      4         if not x.isnumeric():

File ~/.pyenv/versions/3.13.1/envs/datascience/lib/python3.13/site-packages/ipykernel/kernelbase.py:1281, in Kernel.raw_input(self, prompt)
   1279 if not self._allow_stdin:
   1280     msg = "raw_input was called, but this frontend does not support input requests."
-> 1281     raise StdinNotImplementedError(msg)
   1282 return self._input_request(
   1283     str(prompt),
   1284     self._parent_ident["shell"],
   1285     self.get_parent("shell"),
   1286     password=False,
   1287 )

StdinNotImplementedError: raw_input was called, but this frontend does not support input requests.

6.1.3.3. Example Problem 2#

We’ll write a (crude) countdown timer. The user enters a number and the timer will count down that number of seconds.

For this problem, we’ll import a package called time (our first imported package…more on that soon). From the time package, we’ll use a function called sleep which pauses the program for a set number of seconds.

from time import sleep

def countdown(count):
    
    while count > 0:
        print(count)
        count -= 1
        sleep(1)
    print('Times up!')

    

    
countdown(3)
3
2
1
Times up!

6.1.3.4. Example Problem 3#

Let’s write a function that takes a paragraph of text and prints each sentence on it’s own line. We might want to use nested while loops (a while loop inside a while loop) for this one.

Hint: If used in a an if or while statement, a non-empty string evaluates to True. An empty string evaluates to False.

def parse_sentences(text):
    
    # initialize in_quotes (a flag that tells us whether we are in a quotation) and counter k
    in_quotes = False
    k = 0
    
    while text:
        # if we're on the first character of a sentence and it's a space, skip it
        if k==0 and text[0]==' ':
            text = text[1:]
        
        # if the character is the first "", set in_quotes = True and increment
        elif text[k] == '"' and not in_quotes:
            in_quotes = True
            k+=1
            
        # if we hit the closing " or a ., !, or ? print the line and reset variables
        elif (text[k] == '"' and in_quotes) or \
            (text[k] in ['.', '!', '?'] and not in_quotes):
            line = text[0:k+1]
            print(line)
            text = text[k+1:]
            
            k = 0
            in_quotes = False    
        
        # otherwise, move to the next character
        else:
            k+=1
    return
Romeo = 'But, soft! what light through yonder window breaks? It is the east, and Juliet is the sun. Arise, fair sun, and kill the envious moon, Who is already sick and pale with grief, That thou her maid art far more fair than she. It is my lady, O, it is my love! O, that she knew she were! She speaks, yet she says nothing: what of that?'
TheCat = 'We looked and we saw him! The Cat in the Hat! and he said to us, "Why do you sit there like that?" "I know it is wet and the sun is not sunny. But we can have lots of good fun that is funny!"'
parse_sentences(Romeo)
But, soft!
what light through yonder window breaks?
It is the east, and Juliet is the sun.
Arise, fair sun, and kill the envious moon, Who is already sick and pale with grief, That thou her maid art far more fair than she.
It is my lady, O, it is my love!
O, that she knew she were!
She speaks, yet she says nothing: what of that?
parse_sentences(TheCat)
We looked and we saw him!
The Cat in the Hat!
and he said to us, "Why do you sit there like that?"
"I know it is wet and the sun is not sunny. But we can have lots of good fun that is funny!"

6.1.4. For loops#

6.1.4.1. Syntax for For Loops#

for <iter variable> in <iterable set>:
    <code block>
for letter in ['A', 'B', 'C', 'D', 'EaTaI']:
    print(letter.lower())
a
b
c
d
eatai

6.1.4.2. Example Problem 4#

Let’s use a for loop to evaluate a function \(f(x)\) for some list of values of \(x\).

def evalFunc(f, xvals=list(range(0,10))):
    
    yvals = []
    for x in xvals:
        y = f(x)
        yvals.append(y)
        # print(yvals)
               
    return xvals, yvals
def f1(x):
    return x

def f2(x):
    return x**1.5

def f3(x):
    return x**2

x1, y1 = evalFunc(f1, xvals = list(range(-5,6)))
x2, y2 = evalFunc(f2, xvals = list(range(-5,6)))
x3, y3 = evalFunc(f3, xvals = list(range(-5,6)))
y3
[25, 16, 9, 4, 1, 0, 1, 4, 9, 16, 25]
import matplotlib.pyplot as plt  # another package import!

plt.plot(x1, y1, label = 'f1')
plt.plot(x2, y2, label = 'f2')
plt.plot(x3, y3, label = 'f3')

plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.show()
/Users/eatai/.pyenv/versions/3.13.1/envs/datascience/lib/python3.13/site-packages/matplotlib/cbook.py:1719: ComplexWarning: Casting complex values to real discards the imaginary part
  return math.isfinite(val)
/Users/eatai/.pyenv/versions/3.13.1/envs/datascience/lib/python3.13/site-packages/matplotlib/cbook.py:1355: ComplexWarning: Casting complex values to real discards the imaginary part
  return np.asarray(x, float)
../_images/e0f63f6b40857ea005c7aefa991628aa469ff2eda5af9880a2bd59c43b6635c0.png

6.1.4.3. Example Problem 5#

Lists can hold all different types of data. Suppose we get a list of mixed string and numeric data. Write a for loop to sift through the list and create two new lists, one containing all of the strings and another containing all of the numbers.

Hint: Remember type()

type('hello')
str
type(3.45)
float
type(6) == int
True
test_list = ['DS', 256, 'Sec A', 'meets', 'Tuesdays and Thursdays', 11, 30, 'to', 1, 45]

def siftList(jumbleList):
    
    string_list = []
    num_list = []
    
    for item in jumbleList:
        if type(item) == str:
            string_list.append(item)
        elif type(item) == int or type(item) == float:
            num_list.append(item)
                        
    return string_list, num_list

strings, nums = siftList(test_list)

print(strings)
print(nums)
['DS', 'Sec A', 'meets', 'Tuesdays and Thursdays', 'to']
[256, 11, 30, 1, 45]

6.1.4.4. Example Problem 6#

You run an artisan jewelry store on Etsy and you collect data from your customers purchases. A shopping cart is stored as a dictionary with the product name as the key and the value being another dictionary storing quantity and price with keys ‘qty’ and ‘price’ respectively. Write a loop that takes a shopping cart dictionary as input and returns the number of items in the cart and the total cost.

sample_cart1 = dict(bracelet = {'qty':2, 'price':15},
                   necklace = {'qty':1, 'price':45},
                   silver ring = {'qty':2, 'price':25},
                   gold earrings = {'qty':4, 'price':12}
                   )

sample_cart2 = dict(gold ring = {'qty':1, 'price':35},
                   silver ring = {'qty':2, 'price':25},
                   giftcard = {'qty':1, 'price':100}
                   )
  Cell In[22], line 3
    silver ring = {'qty':2, 'price':25},
    ^
SyntaxError: invalid syntax. Perhaps you forgot a comma?

6.1.5. Other loop keywords#

You may exit a loop or skip an iteration of a loop using the break and continue keywords, respectively.

  • break - exit the loop and proceed with code outside of the loop

  • continue - skip the remaining code in the current iteration and advance to the next iteration

Write a function just_numbers that takes as input a list returns a list with only the numerical items from the original list.

Write a function just_upto_numbers that takes as input a list returns a list with only the numerical items from the original list up to the first non-number entry.

def just_numbers(my_list):
    new_list = []
    for item in my_list:
        if type(item)==int or type(item)==float:
            new_list.append(item)
    return new_list

def just_numbers2(my_list):
    new_list = []
    for item in my_list:
        if (not type(item)==int) and (not type(item)==float):
            continue
        new_list.append(item)
        
    return new_list

def just_upto_numbers(my_list):
    new_list = []
    for item in my_list:
        if type(item)==int or type(item)==float:
            new_list.append(item)
        else:
            break
    return new_list
        
test_list = [67, 99, 99.9, 'duck', 1, 5, 'table', '7', 9, 100.1]

list_a = just_numbers(test_list)
list_b = just_numbers2(test_list)

list_c = just_upto_numbers(test_list)

print(list_a)
print(list_b)

print(list_c)
[67, 99, 99.9, 1, 5, 9, 100.1]
[67, 99, 99.9, 1, 5, 9, 100.1]
[67, 99, 99.9]

6.1.5.1. Example Problem 7#

We’ve used the .index() function to find the location of an item in a list. The problem with .index() is that it raises an error if the item isn’t in the list. Let’s write our own version. The function should iterate through a list, checking each entry for a match. If a match is found, the function returns the location of the first match. Otherwise, the function prints ‘no match found’.

def find_idx(my_list, target):
    k = 0
    for item in my_list:
        if item == target:
            return k
        else:
            k+=1
    print('Item not found')
    return None
my_list = ['hello', 'there', 'DS', 256]

find_idx(my_list, 'hi')
find_idx(my_list, 'DS')
Item not found
2

6.1.5.2. Example Problem 8#

Let’s revisit Example Problem 3 above. This time, after a sentence is complete, we should skip any subsequent spaces before beginning a new sentence. Can we achieve this with a continue statement?