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

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

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

>>>
... 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')
0, 1, 2,
3, 4, 5,
6, 7, 8,
9,


## 9.1.10. Assignments¶

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

# list[float]: values from DATA converted to float
result = ...


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

# str: joined DATA values
result = ...


"""
* 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źń'

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


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

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

from unittest.mock import MagicMock

# Simulate user input (for test automation)
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)