While Loops in Python

Learning objectives

In the previous lesson, we learned how Python implements conditionals via if and else statements. We will next move on to work with functions like the pseudocode function Factorial() that we encountered in the core text, which incorporates a while loop.

Factorial(n)
    p ← 1
    i ← 1
    while i ≤ n
        p ← p · i
        i ← i + 1
    return p

Code along summary

Set up

Create a new folder called while_loops in your python/src directory, and then create a new file within the python/src/while_loops folder called main.py, which we will edit in this lesson.

In main.py, let us add the following starter code that is comparable to our starter code in previous lessons.

def main():
    print("While loops.")


if __name__ == "__main__":
    main()

Implementing a factorial function with a while loop

Let’s implement Factorial(). We first set two variables p and i equal to 1. The variable p will store the product of integers and is what we will eventually return.

def factorial(n: int) -> int:
    """
    Takes as input an integer n and returns n! = n*(n-1)*(n-2)*...*2*1 using a while loop.

    Parameters:
    - n (int): an integer

    Returns:
    int: n! = n*(n-1)*(n-2)*...*2*1
    """

    p = 1
    i = 1

    # to be continued

    return p

To create a while loop in Python, we will use the keyword while followed by a colon. The code inside the loop will keep running as long as the condition remains True. Once it becomes False, the loop stops. Each time through the while loop, we update p to be equal to the product of the current value of p and i, then increase i by one. As with if statements, we indent the code block inside the while loop, using consistent indentation (which we suggest to be four spaces for each indent).

def factorial(n: int) -> int:
    """
    Takes as input an integer n and returns n! = n*(n-1)*(n-2)*...*2*1 using a while loop.

    Parameters:
    - n (int): an integer

    Returns:
    int: n! = n*(n-1)*(n-2)*...*2*1
    """

    p = 1
    i = 1

    while i <= n:
        p = p * i
        i = i + 1

    return p

Note that the above function has the exact same structure as our pseudocode factorial function reproduced at the start of the lesson. Regardless of what language we use, we should be able to use the same structure of a while loop increasing a variable i by 1 until it reaches n, with a variable p constantly updated as the product p * i.

Let us call factorial() by appending the following code to def main().

def main():
    print("While loops.")

    n = 5
    m = factorial(n)
    print(m)
STOP: Open a terminal window, and navigate into the folder containing main.py by executing cd python/src/while_loops. Then run your code by executing python3 main.py (macOS/Linux) or python main.py (Windows). You should see 120 printed.
Click Run 👇 to try it!

Handling negative inputs

STOP: Add the line print(factorial(-100)) to the end of main.py. Save and run your code. What happens? Why?
Click Run 👇 to try it!

Currently, if we pass a negative integer as input to factorial(), the function will just return 1, because that is the initial value of p, and the while block is never entered. The problem is that although the type of the variable is correct, its value is not appropriate. In this situation, we should use a raise ValueError() statement, which will print the error message that it is given as input to the console and then immediately end the program. We will see other types of errors later in the course.

def factorial(n: int) -> int:
    """
    Takes as input an integer n and returns n! = n*(n-1)*(n-2)*...*2*1 using a while loop.

    Parameters:
    - n (int): an integer

    Returns:
    int: n! = n*(n-1)*(n-2)*...*2*1
    """

    if n < 0:  # let's handle negative input
        raise ValueError("Error: negative input given to factorial().")

    p = 1
    i = 1

    while i <= n:
        p = p * i
        i = i + 1

    return p
STOP: Save your code with the updated factorial() function, then run your code from the terminal. Previously, calling factorial(-100) would have just returned 1, but now, the program exits with the error message given to ValueError() above. You can now safely remove the line print(factorial(-100)) from main.py.
Click Run 👇 to try it!
Note: You may be wondering why we allow the input factorial(0). 0! is traditionally considered to be equal to 1 because, for any other non-negative n, n! = (n+1)!/(n+1). In this case, when n is 0, 0! = 1!/1, and therefore 0! is equal to 1.

An infinite loop, and a brief musing on programs terminating

Let’s temporarily comment out the line i=i+1 from factorial(), as shown below.

def factorial(n: int) -> int:
    """
    Takes as input an integer n and returns n! = n*(n-1)*(n-2)*...*2*1 using a while loop.

    Parameters:
    - n (int): an integer

    Returns:
    int: n! = n*(n-1)*(n-2)*...*2*1
    """

    if n < 0:  # let's handle negative input
        raise ValueError("Error: negative input given to factorial()!")

    p = 1
    i = 1

    while i <= n:
        p = p * i
        # i = i + 1

    return p

Let’s then run our code using the same def main() as before, reproduced below.

def main():
    print("While loops.")

    n = 5
    m = factorial(n)
    print(m)

When we run this program in the terminal, we see a blinking cursor. The program is running behind the scenes, but it never prints m because it is stuck in an infinite loop within the call to factorial(n), where the statement i <= n is always True.

Click Run 👇 to try it!
Note: You can terminate a process in a terminal window using the command Control+C.

As we start to code larger projects, we will see that it is normal for a function or for a program to take a few minutes, a few hours, or even a few days to finish. This means that it is difficult to discern whether the code is still running or hung in an infinite loop. You might wonder if it is possible to write code to provide a quality assurance check of whether a given program is going to terminate. It turns out that this question lies at the dark heart of computer science, and we will return to it in a later chapter.

Summing the first n integers

Exercise: Write a function sum_first_n_integers() in Python using a while loop that takes an integer n as input and returns the sum of the first n positive integers. As always, we suggest starting on the level of planning in pseudocode.

Our sum_first_n_integers() function is shown below. Note that the structure of this function is very similar to the structure of factorial(). The only difference is that instead of introducing a variable p to store our growing product (which is declared to be equal to 1 and is updated via the command p = p * i each time through the loop), we use a variable s (which is declared to be equal to 0 and is updated via the command s = s + i each time through the loop). We have also added a raise ValueError() statement to handle negative inputs.

def sum_first_n_integers(n: int) -> int:
    """
    Takes as input an integer n and returns the sum of the first n positive integers.

    Parameters:
    - n (int): an integer

    Returns:
    int: sum of the first n positive integers
    """

    if n < 0:
        raise ValueError("Error: negative input given to sum_first_n_integers")

    s = 0
    i = 1

    while i <= n:
        s = s + i
        i = i + 1

    return s

We will now update main() as follows to call sum_first_n_integers().

def main():
    print("While loops.")

    print(sum_first_n_integers(100))
Click Run 👇 to try it!

Syntax shortcuts for incrementing or updating a variable

Before continuing, we make an aside to introduce a few syntactical shorthands for changing the values of variables. Incrementing a variable, or adding one to it, is so common that language designers have built in a special notation for it. Instead of i = i + 1 for incrementing i, we can use the shorthand i += 1. We also have the shorthand i -= 1 to denote i = i - 1.

We can use the shorthand above with any variable instead of a constant 1. For example, the update s = s + i is very common as well. We would like to update a variable (s) by adding something (the current value of i) to the variable’s current value. This statement can be written using the shorthand s += i.

Using these two syntax shorthand tricks, we can update sum_first_n_integers() as follows.

def sum_first_n_integers(n: int) -> int:
    """
    Takes as input an integer n and returns the sum of the first n positive integers.

    Parameters:
    - n (int): an integer

    Returns:
    int: sum of the first n positive integers
    """

    if n < 0:
        raise ValueError("Error: negative input given to sum_first_n_integers")

    s = 0
    i = 1

    while i <= n:
        s += i  # same as s = s + i
        i += 1  # same as i = i + 1

    return s

Python also supports shorthand variable updates corresponding to subtraction, multiplication, and division, as summarized below.

  • s -= i is equivalent to s = s - i;
  • s /= i is equivalent to s = s / i;
  • s *= i is equivalent to s = s * i.

If all these shorthand variable updates look alien to you, that is fine! They may take some getting used to, and in the meantime there is nothing wrong with writing i = i + 1 or sum_val = sum_val + i.

STOP: Use shorthand variable updates to update factorial().

Young Gauss sees a faster algorithm

An apocryphal story about 19th century genius mathematician Carl Friedrich Gauss is that his primary school teacher, annoyed by 10-year-old Carl’s precociousness, set him the task of summing the first 100 positive integers. Gauss quickly responded with the correct answer: 5,050.

Gauss’s insight was to notice that 1 and 100 sum to 101, as do 2 and 99, as do 3 and 98, and so on, ending with 50 and 51. There are 50 such pairs of integers summing to 101, so that the correct answer is 50 · 101 = 5,050. Gauss’s approach can be generalized so that we can compute the sum of the first n integers for any value of n without using any loops at all: the answer is simply n · (n+1)//2.

Gauss’s faster algorithm is shown below. Note that we perform the division n * (n+1) // 2 as an integer division because we should be returning an integer, and if we perform the division as n * (n+1) / 2 instead, then we will return a decimal.

def gauss_sum(n: int) -> int:
    """
    Takes as input an integer n and returns n+(n-1)+(n-2)+...+2+1 using a direct computation.

    Parameters:
    - n (int): an integer

    Returns:
    int: n+(n-1)+(n-2)+...+2+1
    """

    if n < 0:
        raise ValueError("Error: negative input given to gauss_sum().")

    return n * (n+1) // 2
STOP: Print gauss_sum(100) in def main(). Ensure that this function call prints 5050 as Gauss predicted.
Click Run 👇 to try it!

Looking ahead

Before continuing, we point out that you now know enough about loops in Python to implement Euclid’s GCD algorithm. The pseudocode for EuclidGCD() is reproduced below if you would like to give it a shot. If not, we will soon return to show how to implement this algorithm after we discuss how to implement for loops in the next lesson.

EuclidGCD(a,b)
    while a ≠ b
        if a > b
            a ← a − b
        else
            b ← b − a
    return a 

Check your work from the code along

We provide autograders in the window below (or via a direct link) allowing you to check your work for the following functions:

  • factorial()
  • sum_first_n_integers()

powered by Advanced iFrame


Page Contents
Scroll to Top