---
# System prepended metadata

title: Loops (While and For)

---

# Loops (While and For)

The following are condensed notes relating to `while` loops and `for` loops. Example code will be embedded in the notes, but exact output cannot be provided (unlike the HTML notes provided on Canvas). 

Please feel free to copy and run any of the example code in your own python file to observe the output of the code.

We will discuss the implications of `while` loops and `for` loops. 

Practice problems are provided at the end of this document. 

## What is a Loop?

A loop is an operation that allows us to *repeat* a block of code multiple times (control flow). It is very helpful when there is an action or variation of actions we want to repeat. 

There are two ways we can repeat code: a while loop and a for loop.



| Characteristic       | While Loop                        | For Loop                                   |
| -------------------- | -------- | -------- |
| Requirements    | Loop control variable must be defined before the loop | Nothing needs to be defined before starting the loop |
| Starting/Entering the Loop | It is possible to never enter a while loop | The for loop will always execute|
| Number of iterations     | Indefinite (depends on condition)     | Definite (depends on length of collection)     |
| Infinite Loop? | Vulnerable to infinite loops | will never be an infinite loop | 
| Use case | best for when number of iterations is not known. Number of iterations is dependent on condition(s) | best for when number of iterations is known or we want to iterate through a collection of data




## While Loops
While loops are interesting because they **iterate indefinitely**. The condition (or boolean expression) associated with the while loop *controls* the iteration of the loop.

```
while <condition>:
    # do something
```

As long as that condition remains `True`, the loop will continue iterating. This leaves us vulnerable to unwanted **infinite loops**, where our while loop will *never* terminate. 

In order to prevent an infinite loop from occurring, we want to make sure that the condition will evaluate to `False` at some point. To do this,we must **modify the loop control variable** after every iteration of our loop. 

Recall: The loop control variable is the variable(s) associated with the while loop's condition. It is the value(s) that *determine* or *control* whether the condition is `True` or `False`.


### Modifying the Loop Control Variable

In order to prevent an infinite loop from occurring, we must make sure that our while loop's condition will evaluate to false at some point. In order to do this, we must make sure to modify the loop control variable at some point of each iteration. 

Modifying the loop control variable allows our while loop's condition to evaluate new values at each iteration. 

For example, if we had the following loop:
```python=
x = 5
while x > 0:
    print(x)
    x -= 1
```

The loop is evaluated as:
```
before loop: x == 5

while loop begins:
    iteration 1: 
        5 > 0 is True     -- enter loop
            print(5)
            x = 5 - 1     -- x == 4
            
    iteration 2:
        4 > 0 is True     -- enter loop
            print(4)
            x = 4 - 1     -- x == 3
            
    iteration 3:
        3 > 0 is True     -- enter loop
            print(3)
            x = 3 - 1     x == 2
    
    iteration 4:
        2 > 0 is True     -- enter loop
            print(2)
            x = 2 - 1     -- x == 1
    
    iteration 5:
        1 > 0 is True     -- enter loop
            print(1)
            x = 1 - 1     -- x == 0
        
    iteration 6:
        0 > 0 is False    -- do not enter loop, loop terminates
        
```

The condition is `True` 5 times (for 5 iterations). On the 6th iteration, the condition becomes `False`, and the loop terminates. 

`x -= 1` subtracts 1 from `x` during each iteration, modifying the values being evaluated by the while loop's condition so that the condition is eventually false. 


**Note:** We must be careful with how we modify the loop control variable. We must make sure that we modify it in a way that ensured the condition will become `False` at some point. 

### What else can we do with a while loop?

We can consider our loop bodies (aka code blocks) like any other piece of code. We can do any operations we need to do in our loop. A loop is just action(s) we want to repeat some number of times.

We can do anything we want in the body of a for loop! Any code that we want to repeat can be put in the body of for loops. That includes arithmetic expressions, use of functions (e.g. print(), input(), etc.), assignment statements, if/elif/else blocks, etc...


## For Loops

For loops are useful because they loop for a **definite** number of times. The number of iterations depends on the number of elements contained in the given collection type. 

```python=
for temp in collection:
    # loop body
```

The for loop iterates over the elements of the given collection. For each iteration of the for loop, the `temp` variable holds the value of the current element. When there are no more elements for the loop to iterate over, the loop terminates. 

For example, given the following code:
```python=
for letter in "school":
    print(letter)
```

The loop is evaluated as:
```
iteration 1: letter == "s"
    print("s")

iteration 2: letter == "c"
    print("c")
    
iteration 3: letter == "h"
    print("h")
    
iteration 4: letter == "o"
    print("o")
    
iteration 5: letter == "o"
    print("o")

iteration 6: letter == "l"
    print("l")
```

There are 6 characters (elements) in the string "school", so our for loop iterates 6 times (once for each element). When we say `print(letter)`, we are really saying `print the current element of the loop`.

**What is a collection type?** 
A collection type is a data type that is made up of a *collection* of other data. 


| Collection Type | Example | Elements of the collection |
| -------- | -------- | -------- |
| String     | "Hello"     | each individual character is an element -- "h" "e" "l" "l" "o"     |
| List    | [1,2,3]    | Each item/data separated by a comma is an element
| range()    | range(10)    | range isn't a collection type, but it allows us to iterate over a collection/sequence of integers 

Future collection types...


| Collection Type | Example           | Elements of the Collection                        |
| --------------- |:----------------- | ------------------------------------------------- |
| Tuples          | (1,2,3)           | Each item/data separated by a comma is an element |
| Dictionaries    | {1 : "a", 1: "b"} | Each key:value pair separated by a comma is an element |
| Sets | {1,2,3} | Each item/data separated by a comma is an element |

### What else can you do with a for loop?
Similarly to while loops, we can do anything we want in the body of a for loop! Any code that we want to repeat can be put in the body of for loops. That includes arithmetic expressions, use of functions (e.g. print(), input(), etc.), assignment statements, if/elif/else blocks, etc... It all just depends on what we need our loop to do!

## Break and Continue in Loops

Sometimes, we don't want to execute every iteration. The `break` and `continue` statements can be used in loops to interrupt the flow of our loop. 

### The break statement
The break statement is used to "break" out of a loop prematurely. If a break statement is encountered, it will cause the loop to terminate (or "break out"), even if the loop wasn't finished (i.e. the condition of the while loop was still True or there were still elements left in the for loop).

However, we want to be mindful of when we use the break statement. We want to use the break statement only when there is a reason to break (some condition is met). That is, we want to use a break statement only in if/elif/else blocks.

If we had a break statement NOT in an if/elif/else block, the loop will always terminate during the first iteration:

```python=
# while loop
x = 0
while x < 3:
    print(x)
    x += 1
    break
    
# for loop
for x in range(1,4):
    print(x)
    break
```

If you were to run the above code, each loop would iterate only one time before encountering the break statement, causing the loop to terminate.


Instead, we would approach the break statement as such (a bit of a more intricate example):

> Prompt: We want to simulate playing a dice rolling game. Each player gets to roll the dice 3 times per turn. The value of each roll gets added to your total points for the game. However, if you roll a double (e.g. both die land on the same number), your turn ends.
> 
> We want to write some code to simulate one turn. We can roll up to 3 times, but if we get a double, our turn ends. After each roll, we can add our points to our total

Our code might look like:

```python=
import random        # to simulate dice rolls

turn_points = 0

for x in range(3):
    die_roll_1 = random.randint(1,6)    # "roll" a number 1-6
    die_roll_2 = random.randint(1,6)    # "roll" a number 1-6
    
    if die_roll_1 == die_roll_2:        # check if rolled same num
        break        # if yes, turn ends,
    
    # die_roll_1 and die_roll_2 are different values
    # and we can add them to our turn_points
    turn_points += die_roll_1 + die_roll_2        
```

Explanation: 
* Each turn gets 3 rolls, so we want to iterate only 3 times (max). 
* During each turn, we want to roll our two die. We use the random.randint(1,6) operation to generate a random number between 1-6, similar to how we would get a random number between 1-6 if we rolled a die
* Before we can add our points together, we must check that we "rolled" two unique values.
    * If both die rolled the same value, we do not get the points for the roll and our turn ends. We represent this through the break statement
    * If both die do not roll the samve value, we get the points and can continue to our next roll (if we have any left)


*Question*: Why am I able to add the points (`turn_points += die_roll_1 + die_roll_2` on line 14) without using an `else` statement?

Answer: [^1].

[^1]: If the if statement, `if die_roll_1 == die_roll_2:` is True, then we will encounter the break statement and the loop will terminate. Thus, it does not matter what comes after that if statement if the if statement's condition is True. 

    In other words, due to the break statement, we know that line 14 will execute only when the if statement is false, making an else statement redundant.

*Question*: Can you rewrite the loop into a while loop?

Answer: [^2]

[^2]: The while loop might look something like:
    
    ```python=
    import random
        
    roll_count = 0
    turn_points = 0
        
    while roll_count < 3:
        die_roll_1 = random.randint(1,6)
        die_roll_2 = random.randint(1,6)
            
        if die_roll_1 == die_roll_2:
            break
            
        turn_points += die_roll_1 + die_roll_2
            
        roll_count += 1
    ```



### The continue statement

The continue statement is used to "continue" to the next iteration of the loop. If a continue statement is encountered, it will cause the loop to stop/skip the rest of the current iteration and jump to or "continue" to the next iteration of the loop.

Unlike the while loop, it does not terminate the loop. It just skips to the next iteration of the loop, no matter if there was code left in the current iteration.

Similarly to while loops, we want to be mindful when we use the continue statement. We want to use the continue statement only when there is a reason to continue/skip the current iteration (some condition is met). That is, we want to use the continue statement only in if/elif/else blocks. 

If we had a continue statement NOT in an if/elif/else block, the loop will also skip to the next iteration. This would be especially disruptive if the condition statement is in the middle of the loop body. See example code below:

```python=
# while loop
x = 0
while x < 3:
    print(x)
    continue
    x += 1
    
# for loop
for x in range(1,4)
    print(x)
    continue
```

Notice a few things about the above code. If you were to run the while loop, we would get an infinite loop despite having a line of code that modifies the loop control variable. Why is that? (Answer: [^3])

[^3]: It is becuase the continue statement is encountered before we modify the loop control variable, so we never get to that line. That means for every iteration of the while loop, the value of the loop control variable (x) does not change, resulting in an infinite loop.

On the other hand, the for loop does not seem to be affected (because the continue statement is at the end of the loop body), but what would happen if we moved the continue statement?

```python=
for x in range(1,4):
    continue
    print(x)
```


All of this is to say: **only use continue in an if/elif/else block**. Please.


Let's see an interesting example of the continue statement at play:
> Prompt: We are playing the dice game again, but the rule for rolling a double has changed. Instead of our turn ending if we rolled a double, we will just not get the points for that roll. This will allow us to have all three rolls per turn.
> 
> So, if we roll a double, we skip those points and continue to our next roll (if we have any)

Our code might look like:
```python=
import random

turn_points = 0
for x in range(3):
    roll_1 = random.randint(1,6)
    roll_2 = random.randint(1,6)
    
    if roll_1 == roll_2:
        continue
        
    turn_points += roll_1 + roll_2
```


**We must be extra mindful if we are using the continue statement in a while loop.**
If we use continue in a while loop, we must make sure that our loop control variable is still being modified, or else we will end up with an infinite loop. 

The easiest way to do this would be to add statement *before* the continue statement that modifies the loop control variable:
```python=
import random

rolls = 0
turn_points = 0

while rolls < 3: 
    roll_1 = random.randint(1,6)
    roll_2 = random.randint(1,6)
    
    if roll_1 == roll_2:
        rolls += 1 
        continue 
    
    turn_points += roll_1 + roll_2
    rolls += 1
```

## Loops Practice Material

The following are small prompts to practice your understanding of while loops and for loops. The answer key linked to each question.

Remember, there are often many ways to write a piece of code. If your answer looks different from mine but still works as expected, it can still be valid!


1. **Prompt**: given a list of names, loop through the list and print the message "I hope you have a great day, {name}!" The list is provided below: (Answer: [^4])
```python=
names = ["Jackson", "Miles", "Jessica", "Emma", "Taylor", "Frank"]
```

[^4]: Answer:
    ```python=
    names = ["Jackson", "Miles", "Jessica", "Emma", "Taylor", "Frank"]
    
    for name in names:
        print(f"I hope you have a great day, {name}!")

    ```



2. **Prompt**: Using a for loop and range(), calculate the sum of all the even numbers between 1 and 255. Print out the result. (Answer: [^5])

[^5]: Answer:
    ```python=
    sum = 0
    for x in range(1,255):
        if x % 2 == 0:
            sum += x
    
    print(f"The sum of all even numbers between 1 and 255 is: {sum}")
    ```

3. **Prompt**: Using a for loop, count the number of a's, e's, i's, o's, and u's in a string that you ask the user to input. Then print out a sentence that tells the user the counts. (Answer[^6])

[^6]: Answer:
    ```python=
    user_string = input("Enter a word or sentence: ")
    count_vowels = 0
    
    for character in user_string:
        if user_string in "aeiouAEIOU":
            count_vowels += 1
            
    print(f"There were {count_vowels} vowels in your string!")
        
    ```

4. **Prompt**: Using a while loop, print out all the numbers between 1 and 200 that are divisible by both 5 and 9. (Answer[^7])

[^7]: Answer
    ```python=
    x = 1
    count = 0
    
    while x <= 200:
        if x % 5 == 0 and x % 9 == 0:
            count += 1
            
    print(f"There are {count} numbers between 1 and 200 that are divisible by 5 and 9")
    ```

5. **Prompt**: Write a program that asks the user to guess your age. If they guess too high, print out "wow, do I look that old?". If they guess too low, print out "hahaha don't I look wiser than that?". Once they guess correctly, print out a message telling them and exit the loop. (Answer[^8])

[^8]: Answer:
    ```python=
    correct_age = 24
    
    user_guess = int(input("Guess my age: "))
    
    while user_guess != correct_age:
        if user_guess > correct_age:
            print("wow, do I look that old?")
        elif user_guess < correct_age:
            print("hahaha don't I look wiser than that?")
            
        user_guess = int(input("Guess my age: "))
        
    
    # if we're out of the loop, they guessed it correctly
    print("congrats! you guess right!")
    ```