7.1. Loop While

7.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

7.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

7.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

7.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')

7.1.5. Until

Has stop conditions

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

7.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

7.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!

7.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

7.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,

7.1.10. Assignments

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

English:
    1. Use data from "Given" section (see below)
    2. Create `result: list[float]`
    3. Use `while` to iterate over `DATA`
    4. Convert current elements of `DATA` to `float`
    5. Converted value append to `result`
    6. Compare result with "Tests" section (see below)

Polish:
    1. Użyj danych z sekcji "Given" (patrz poniżej)
    2. Stwórz `result: list[float]`
    3. Użyj `while` do iterowania po `DATA`
    4. Przekonwertuj obecny element `DATA` do `float`
    5. Przekonwertowaną wartość dodaj na koniec `result`
    6. Porównaj wyniki z sekcją "Tests" (patrz poniżej)

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]
"""

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

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

English:
    1. Use data from "Given" section (see below)
    2. Create `result: str`
    3. Use `while` to iterate over `DATA`
    4. Add current element of `DATA` to `result`
    5. Do not use `str.join()`
    6. Compare result with "Tests" section (see below)

Polish:
    1. Użyj danych z sekcji "Given" (patrz poniżej)
    2. Stwórz `result: str`
    3. Użyj `while` do iterowania po `DATA`
    4. Dodaj obecny element z `DATA` do `result`
    5. Nie używaj `str.join()`
    6. Porównaj wyniki z sekcją "Tests" (patrz poniżej)

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

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

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

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

English:
    1. Use data from "Given" section (see below)
    2. Use `while` to iterate over `DATA`
    3. If letter is in `PL` then use conversion value as letter
    4. Add letter to `result`
    5. Compare result with "Tests" section (see below)

Polish:
    1. Użyj danych z sekcji "Given" (patrz poniżej)
    2. Użyj `while` do iteracji po `DATA`
    3. Jeżeli litera jest w `PL` to użyj skonwertowanej wartości jako litera
    4. Dodaj literę do `result`
    5. Porównaj wyniki z sekcją "Tests" (patrz poniżej)

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

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

# Given
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 7.4. Solution
"""
* Assignment: Loop While Input
* Complexity: medium
* Lines of code: 14 lines
* Time: 13 min

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

Polish:
    1. Użyj danych z sekcji "Given" (patrz poniżej)
    2. Zdefiniuj `grades: list[float]`
    3. Do iterowania użyj tylko pętli `while`
    4. Używając `input()` poproś użytkownika o ocenę, jedną na raz
    5. Użytkownik poda tylko poprawne `int` lub `float`
    6. Jeżeli ocena jest w `GRADE_SCALE` - dodaj ją do `grades`
    7. Jeżeli oceny nie ma w `GRADE_SCALE`, pomiń tą iterację
    8. Jeżeli użytkownik wcisnął Enter, zakończ wprowadzanie danych
    9. Na zakończenie zdefiniuj `result` z wyliczeniem średniej
       arytmetycznej `grades`
    10. Przetestuj przypadek, gdy dzienniczek jest pusty

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

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

    >>> 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
"""

# Mock input() built-in function
from unittest.mock import MagicMock
input = MagicMock(side_effect=['1', '2', '2.5', '3', '3.5', '4', '5', '6', ''])


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

result = ...  # float: arithmetic mean of grades