9.1. Loop While

9.1.1. Rationale

>>> data = ['a', 'b', 'c']
>>>
>>> data[0]
'a'
>>> data[1]
'b'
>>> data[2]
'c'
>>> data = ['a', 'b', 'c']
>>> i = 0
>>>
>>> if i < 3:
...     print(data[i])
...     i += 1
a
>>>
>>> if i < 3:
...     print(data[i])
...     i += 1
b
>>>
>>> if i < 3:
...     print(data[i])
...     i += 1
c
>>> data = ['a', 'b', 'c']
>>> i = 0
>>>
>>> while i < 3:
...     print(data[i])
...     i += 1
a
b
c

9.1.2. Syntax

  • Continue execution when argument is True

  • Stops if argument is False

while loop generic syntax:

while <condition>:
    <do something>
>>> # doctest: +SKIP
... while True:
...     pass

9.1.3. Convention

  • The longer the loop scope, the longer the variable name should be

  • Avoid one letters if scope is longer than one line

  • Generic names:

    • i - for loop counter

    • abort - for abort flags

    • abort_flag - for abort flags

9.1.4. Infinite Loop

Never ending loop. Used in servers to wait forever for incoming connections. Used in games for game logic.

>>> # doctest: +SKIP
... while True:
...     print('hello')

9.1.5. Until

Has stop conditions

>>> i = 0
>>>
>>> while i < 3:
...     print(i)
...     i += 1
0
1
2

9.1.6. Iterating Over Sequence

Better idea for this is to use for loop. for loop supports Iterators. len() must write all numbers to memory, to calculate its length:

>>> i = 0
>>> data = ['a', 'b', 'c']
>>>
>>> while i < 3:
...     print(i, data[i])
...     i += 1
0 a
1 b
2 c
>>> i = 0
>>> data = ['a', 'b', 'c']
>>> length = 3
>>>
>>> while i < length:
...     print(i, data[i])
...     i += 1
0 a
1 b
2 c
>>> i = 0
>>> data = ['a', 'b', 'c']
>>>
>>> while i < len(data):
...     print(i, data[i])
...     i += 1
0 a
1 b
2 c

9.1.7. Exit flag

Exit flag pattern is useful if you have for example multi-threaded application:

>>> abort = False
>>> i = 10
>>>
>>> while not abort:
...     print(i)
...     i -= 1
...
...     if i == 6:
...         print('Fuel leak detected. Abort, Abort, Abort!')
...         abort = True
10
9
8
7
Fuel leak detected. Abort, Abort, Abort!

9.1.8. Force Exit the Loop

Force exit the loop using break keyword:

>>> i = 10
>>>
>>> while True:
...     print(i)
...     i -= 1
...
...     if i == 6:
...         print('Fuel leak detected. Abort, Abort, Abort!')
...         break
10
9
8
7
Fuel leak detected. Abort, Abort, Abort!

Exiting the loop using break keyword:

>>> # doctest: +SKIP
... while True:
...     number = input('Type number: ')
...
...     if not number:
...         # if user hit enter
...         # without typing a number
...         break

9.1.9. Force Skip Iteration

  • if continue is encountered, it will jump to next loop iteration

>>> TEXT = ['# "Moon Speech" by John F. Kennedy, Rice Stadium, Houston, TX, 1962-09-12',
...         '# Source: http://er.jsc.nasa.gov/seh/ricetalk.htm',
...         'We choose to go to the Moon.',
...         'We choose to go to the Moon in this decade and do the other things.',
...         'Not because they are easy, but because they are hard.',
...         'Because that goal will serve to organize and measure the best of our energies a skills.',
...         'Because that challenge is one that we are willing to accept.',
...         'One we are unwilling to postpone.',
...         'And one we intend to win']
>>>
>>> i = 0
>>>
>>> while i < len(TEXT):
...     line = TEXT[i]
...     i += 1
...
...     if line.startswith('#'):
...         continue
...
...     print(line)
We choose to go to the Moon.
We choose to go to the Moon in this decade and do the other things.
Not because they are easy, but because they are hard.
Because that goal will serve to organize and measure the best of our energies a skills.
Because that challenge is one that we are willing to accept.
One we are unwilling to postpone.
And one we intend to win

Force skip iteration using continue keyword:

>>> i = 0
>>>
>>> while i < 10:
...     print(i, end=', ')
...     i += 1
...
...     if i % 3:
...         continue
...
...     print(end='\n')  # doctest: +NORMALIZE_WHITESPACE
0, 1, 2,
3, 4, 5,
6, 7, 8,
9,

9.1.10. Assignments

Code 9.1. Solution
"""
* Assignment: Loop While to Float
* Required: yes
* Complexity: easy
* Lines of code: 5 lines
* Time: 5 min

English:
    1. Create `result: list[float]`
    2. Use `while` to iterate over `DATA`
    3. Convert current elements of `DATA` to `float`
    4. Converted value append to `result`
    5. Run doctests - all must succeed

Polish:
    1. Stwórz `result: list[float]`
    2. Użyj `while` do iterowania po `DATA`
    3. Przekonwertuj obecny element `DATA` do `float`
    4. Przekonwertowaną wartość dodaj na koniec `result`
    5. Uruchom doctesty - wszystkie muszą się powieść

Hints:
    * `Stop` or `Ctrl+C` kills infinite loop

Tests:
    >>> import sys; sys.tracebacklimit = 0

    >>> type(result)
    <class 'list'>

    >>> assert all(type(x) is float for x in result)

    >>> result
    [2.0, 3.0, 3.5, 4.0, 4.5, 5.0]
"""

DATA = (2, 3, 3.5, 4, 4.5, 5)
result = ...  # list[float]: values from DATA converted to float

Code 9.2. Solution
"""
* Assignment: Loop While to Str
* Required: yes
* Complexity: easy
* Lines of code: 4 lines
* Time: 5 min

English:
    1. Create `result: str`
    2. Use `while` to iterate over `DATA`
    3. Add current element of `DATA` to `result`
    4. Do not use `str.join()`
    5. Run doctests - all must succeed

Polish:
    1. Stwórz `result: str`
    2. Użyj `while` do iterowania po `DATA`
    3. Dodaj obecny element z `DATA` do `result`
    4. Nie używaj `str.join()`
    5. Uruchom doctesty - wszystkie muszą się powieść

Hints:
    * `Stop` or `Ctrl+C` kills infinite loop

Tests:
    >>> import sys; sys.tracebacklimit = 0

    >>> type(result)
    <class 'str'>
    >>> result
    'hello'
"""

DATA = ['h', 'e', 'l', 'l', 'o']
result = ...  # str: joined DATA values

Code 9.3. Solution
"""
* Assignment: Loop While Translate
* Required: yes
* Complexity: medium
* Lines of code: 5 lines
* Time: 5 min

English:
    1. Use `while` to iterate over `DATA`
    2. If letter is in `PL` then use conversion value as letter
    3. Add letter to `result`
    4. Run doctests - all must succeed

Polish:
    1. Użyj `while` do iteracji po `DATA`
    2. Jeżeli litera jest w `PL` to użyj skonwertowanej wartości jako litera
    3. Dodaj literę do `result`
    4. Uruchom doctesty - wszystkie muszą się powieść

Hints:
    * `Stop` or `Ctrl+C` kills infinite loop

Tests:
    >>> import sys; sys.tracebacklimit = 0

    >>> type(result)
    <class 'str'>
    >>> result
    'zazolc gesla jazn'
"""

PL = {
    'ą': 'a',
    'ć': 'c',
    'ę': 'e',
    'ł': 'l',
    'ń': 'n',
    'ó': 'o',
    'ś': 's',
    'ż': 'z',
    'ź': 'z',
}

DATA = 'zażółć gęślą jaźń'

result = ...  # str: DATA with substituted PL diacritic chars to ASCII letters

Code 9.4. Solution
"""
* Assignment: Loop While Input
* Required: no
* Complexity: medium
* Lines of code: 14 lines
* Time: 13 min

English:
    1. Define `grades: list[float]`
    2. Using `input()` ask user about grade, one at a time
    3. User will type only valid `int` or `float`
    4. To iterate use only `while` loop
    5. If grade is in `GRADE_SCALE` - add it to `grades`
    6. If grade is not in `GRADE_SCALE`, skip this iteration
    7. If user pressed Enter key, end inserting data
    8. Define `result: float` with arithmetic mean of `grades`
    9. Test case when report list is empty
    10. Run doctests - all must succeed

Polish:
    1. Zdefiniuj `grades: list[float]`
    2. Do iterowania użyj tylko pętli `while`
    3. Używając `input()` poproś użytkownika o ocenę, jedną na raz
    4. Użytkownik poda tylko poprawne `int` lub `float`
    5. Jeżeli ocena jest w `GRADE_SCALE` - dodaj ją do `grades`
    6. Jeżeli oceny nie ma w `GRADE_SCALE`, pomiń tą iterację
    7. Jeżeli użytkownik wcisnął Enter, zakończ wprowadzanie danych
    8. Zdefiniuj `result: float` ze średnią arytmetyczą `grades`
    9. Przetestuj przypadek, gdy dzienniczek jest pusty
    10. Uruchom doctesty - wszystkie muszą się powieść

Hints:
    * `Stop` or `Ctrl+C` kills infinite loop
    * `mean = sum(...) / len(...)`

Tests:
    >>> import sys; sys.tracebacklimit = 0
    >>> from statistics import mean

    >>> type(grades)
    <class 'list'>
    >>> type(result)
    <class 'float'>

    >>> assert all(type(x) is float for x in grades)

    >>> mean(grades) == result  # doctest: +SKIP
    True

    >>> result
    3.5
"""

# Simulate user input (for test automation)
from unittest.mock import MagicMock
input = MagicMock(side_effect=['1', '2', '2.5', '3', '3.5', '4', '5', '6', ''])


GRADE_SCALE = (2.0, 3.0, 3.5, 4.0, 4.5, 5.0)

grades = ...  # list[float]: all user grades
result = ...  # float: arithmetic mean of grades