## while loop¶

This example introduces a new kind of loop -- a while loop

### Structure of a while loop¶

    while expression:
body



What it does

    Check whether the Boolean expression (also known as the condition) is True.
If it is, execute the body of the loop and go back to the loop header.
Check again...



Important Note: If the condition becomes false during the body of the loop, the loop does not stop at that moment. The only time a decision about whether to continue or stop is made is when the loop header is reached.

### Example using while¶

A list contains the patient's report about their well-being for each day. Return the number of days it takes for the patient to say "I'm fine".

For example:

reports = ["My leg hurts",
"I'm tired",
"I'm sleepy",
"Awesome!",
"Hi Doctor, I'm fine",



For the list above, it takes 4 days for the patient to say "I'm fine".

Here is how to compute that using a while-loop:

In [4]:
reports = ["My leg hurts",
"I'm tired",
"I'm sleepy",
"Awesome!",
"Hi Doctor, I'm fine",

n_days = 0
while reports[n_days].find("I'm fine") == -1:
n_days += 1

print(n_days)

4


What happens when the phrase "I'm fine" does not occur in reports? In the solution above, the find method will return -1 and the loop exits.

### Loops Conditions and Lazy Evaluation¶

The problem: print the characters of str s, up to the first vowel in s.

The first attempt at solving this problem works nicely when s contains one or more vowels:

In [11]:
i = 0
s = 'CTTCAGACCTTG'
while not (s[i] in 'aeiouAEIOU'):
print(s[i])
i = i + 1

C
T
T
C


However, an error occurs when the string does not contain any vowels:

In [10]:
i = 0
s = 'xyz'
while not (s[i] in 'aeiouAEIOU'):
print(s[i])
i = i + 1

x
y
z

--------------------------------------------------------------------------
IndexError                               Traceback (most recent call last)
<ipython-input-10-d7960e01dace> in <module>()
1 i = 0
2 s = 'xyz'
----> 3 while not (s[i] in 'aeiouAEIOU'):
4     print(s[i])
5     i = i + 1

IndexError: string index out of range

In the code above, the error occurs when s is indexed at i and i is outside of the range of valid indices. To prevent this error, add an additional condition is added to ensure that i is within the range of valid indices for s:

In [9]:
i = 0
s = 'xyz'
while i < len(s) and not (s[i] in 'aeiouAEIOU'):
print(s[i])
i = i + 1

x
y
z


The order of the conditions matters. In Python, when the first operand to the and operator is False, the second operand is not evaluated. Therefore, when i >= len(s), the first operand is False, so the second operand isn't evaluated and the index out of range error is avoided.

### Practice Exercise: checking for valid input¶

a) Write a function yes_or_no that asks a user to enter either 'yes' or 'no' and keeps looping asking again and again until the user enters one of these two options.

b) Now, change your function so that it accepts any case variation such as 'Yes', 'YES' or even 'nO' and then returns the lowercase version of what the user provided. But if the user inputs 'nope' or 'maybe' or anything else, it doesn't return and asks again for 'yes' or 'no'.

### Practice Exercise: a better guessing game¶

In a previous session, we wrote code to guess a number. If the user guessed wrong, we reported Too high, Too low, or You got it:

In [13]:
# The first two lines generate a random integer between 1 and 10.
import random
secret = random.randint(1, 10)

guess = int(input('Enter a number between 1 and 10: '))
if guess == secret:
print('You got it')
elif guess > secret:
print('Too high')
else:
print('Too low')

Enter a number between 1 and 10: 5
Too high


Now, rather than have the user guess only once, prompt the user to guess until they get it right.