While loops

Download exercises zip

Browse online files

Let’s see how to repeat instructions by executing them inside while loops.

The main feature of while loop is allowing to explicitly control when the loop should end. Typically, such loops are used when we must iterate on a sequence of which we don’t know the dimension, or it can vary over time, or several conditions might determine the cycle stop.

What to do

  1. Unzip exercises zip in a folder, you should obtain something like this:

while
    while1.ipynb
    while1-sol.ipynb
    while2-chal.ipynb
    jupman.py

WARNING: to correctly visualize the notebook, it MUST be in an unzipped folder !

  1. open Jupyter Notebook from that folder. Two things should open, first a console and then a browser. The browser should show a file list: navigate the list and open the notebook while.ipynb

  2. Go on reading the exercises file, sometimes you will find paragraphs marked Exercises which will ask to write Python commands in the following cells.

Shortcut keys:

  • to execute Python code inside a Jupyter cell, press Control + Enter

  • to execute Python code inside a Jupyter cell AND select next cell, press Shift + Enter

  • to execute Python code inside a Jupyter cell AND a create a new cell aftwerwards, press Alt + Enter

  • If the notebooks look stuck, try to select Kernel -> Restart

Counting with a while

A while cycle is a code block which is executed when a certain boolean condition is verified. The code block is repeatedly executed as long as the condition is true.

Let’s see an example:

[2]:
i = 1

while i < 4:
    print('Counted', i)
    i += 1

print('Loop is over!')
Counted 1
Counted 2
Counted 3
Loop is over!

In the example, the boolean condition is

i < 4

the block to repeatedly executed is

print('Counted', i)
i += 1

Like any Python code blocks, the block is indented with spaces (usually 4).

Have a better look at the execution in Python Tutor and read the following comment.

[3]:
# WARNING: FOR PYTHON TUTOR TO WORK, REMEMBER TO EXECUTE THIS CELL with Shift+Enter
#          (it's sufficient to execute it only once)

import jupman
[4]:
i = 1
while i < 4:
    print('Counted', i)
    i += 1

print('Loop is over !')

jupman.pytut()
Counted 1
Counted 2
Counted 3
Loop is over !
[4]:

In the example we used a variable we called i and we initialized it to zero.

At the beginning of the cycle i is valued 1, so the boolean expression i < 4 is evaluated as True. Since it’s True, execution continues inside the block with the print and finally MODIFIES i by incrementing i += 1.

Now the execution goes to while row, and condition i < 4 is evaluated again. At this second iteration i is valued 2, so the boolean expression i < 4 is again evaluated to True and the execution remains inside the block. A new print is done and i gets incremented.

Another loop is done until i is valued 4. A that point i < 4 produces False so in that moment execution exits the while block and goes on with the commands at the same indentation level as the while

Terminating while

When we have a while cycle, typically sooner or later we want it to terminate (programs which hang aren’t users’ favourites …). To guarantee termination, we need:

  1. initializing a variable outside the cycle

  2. a condition after the while command which evaluates that variable (and optionally other things)

  3. at least one instruction in the internal block which MODIFIES the variable, so that sooner or later is going to satisfy condition 2

If any of these points is omitted, we will have problems. Let’s try forgetting them on purpose:

Error 1: omit initialization. As in those cases in Python where we forgot to initialize a variable (let’s try j in this case), the execution is interrupted as soon we try using the variable:

print("About to enter the cycle ..")
while j < 4:
    print('Counted', j)
    j += 1

print('Loop is over !')
About to enter the cycle ..
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-277-3f311955204d> in <module>()
      1 print("About to enter the cycle ..")
----> 2 while j < 4:
      3     print('Counted', j)
      4     j += 1
      5

NameError: name 'j' is not defined

Error 2: omit using the variable in the condition. If we forget to evaluate the variable, for example using another one by mistake (say x), the loop will never stop:

i = 1
x = 1
print('About to enter the cycle ..')
while x < 4:   # evalutes x instead of i
    print('Counted', i)
    i += 1

print('Loop is over !')
About to enter the cycle ..
Counted 1
Counted 2
Counted 3
Counted 4
Counted 5
Counted 6
.
.

Error 3: Omit to MODIFY the variable in the internal block. If we forget to place at least one instruction which MODIFIES the variable used in the condition, whenever the condition is evaluated it will always produce the same boolean value False preventing a cycle exit:

i = 1
print('About to enter the cycle ..')
while i < 4:
    print('Counted', i)

print('Loop is over !')
About to enter the cycle ..
Counted 1
Counted 1
Counted 1
Counted 1
Counted 1
.
.

Non terminating while

QUESTION: Can you imagine a program which never terminates?

Show answer

Questions

QUESTION: Look at the following code fragments , and for each try guessing the result it produces (or if it gives an error):

  1. i = 0
    while i < 3:
    print(i)
    
  2. k = 0
    while k < 5:
        print(k)
        k + 1
    
  3. i = 0
    while i < 3:
        print(i)
    i += 1
    
  4. i = 0
    while False:
        print(i)
        i += 1
    print('Done !')
    
  5. i = 0
    while i < 3:
        print(i)
        i += 1
    
  6. k = 0
    while k < 2
        print(i)
        k += 1
    
  7. i = 0
    while i < 3:
        print('GAM')
        i = i + 1
    
  8. while zanza < 2
        print('ZANZA')
        zanza += 1
    
  9. i = 0
    while False:
        print(i)
        i = i + 1
    print('DARK')
    
  10. i = 0
    while True:
        print(i)
        i = i + 1
    print('LIGHT')
    
  11. while 2 + 3:
        print('z')
    print('')
    
  12. i = 10
    while i > 0:
        if i > 5:
            print(i)
            i -= 1
    print('WAM')
    
  13. i = 10
    while i > 0:
        if i > 5:
            print(i)
        i -= 1
    print('MAW')
    
  14. import random
    x = 0
    while  x < 7:
        x = random.randint(1,10)
        print(x)
    
    print('LUCK')
    
  15. x,y = 0,0
    while x + y < 4:
        x += 1
        y += 1
        print(x,y)
    
  16. x,y = 0,3
    while x < y:
        print(x,y)
        x += 1
        y -= 1
    

Esercises

Exercise - printeven

✪ Write some code to print all the odd numbers from 1 to k in a while cycle

  • for k<1 prints nothing

Example - given:

k = 5

after your code it must print:

1
3
5
Show solution
[5]:

k = 5   # 1 3 5
#k = 1  # 1
#k = 0  # no print

# write here


Exercise - average

✪ Write some code that given a list numbers, calculates the average of values using a while and then prints it.

  • if the list is not empty, the average is supposed to be 0.0

  • DO NOT use the function sum

  • DO NOT create variables called sum (would violate the V COMMANDMENT: you shall never ever redefine system functions)

Example - given:

numbers = [8,6,5,9]

prints

7.0
Show solution
[6]:

numbers = [8,6,5,9] # 7.0
#numbers = [3,1,2]  # 2.0
#numbers = []       # 0

# write here


break and continue commands

For getting even more control on cycle execution we can use the commands break and continue

NOTE: Use them sparingly!

When there is a lot of code in the cycle it’s easy to ‘forget’ about their presence and introduce hard-to-discover bugs. On the other hand, in some selected cases these commands may increase code readability, so as everything use your judgement.

Terminate with a break

The scheme we’ve seen to have a terminating while is the recommended one, but if we have a condition which does NOT evaluate the variable we are incrementing (like for example the constant expression True), as an alternative to immediatly exit the cycle we can use the command break:

[7]:
i = 1
while True:

    print('Counted', i)

    if i > 3:
        print('break! Exiting the loop!')
        break
        print('After the break')

    i += 1

print('Loop is over !')
Counted 1
Counted 2
Counted 3
Counted 4
break! Exiting the loop!
Loop is over !

Note After the break is not shown.

Jumping with continue

We can bring the execution immediately to the next iteration by calling continue, which directly jumps to the condition check without executing the instructions after the continue.

WARNING: continue instructions if used carelessly can cause infinite loops !

When using continue ensure it doesn’t jump the instriction which modifies the variable used in the termination condition (or it doesn’t jump a break needed for exiting the cycle)!

To avoid problems here we incremented i before the if with a continue:

[8]:
i = 1
while i < 5:
    print('Counted', i)

    i += 1

    if i % 2 == 1:
        print('continue, jumping to condition check')
        continue
        print('After the continue')

    print('arrived till the end')

print('Loop is over !')
Counted 1
arrived till the end
Counted 2
continue, jumping to condition check
Counted 3
arrived till the end
Counted 4
continue, jumping to condition check
Loop is over !

Let’s try combining break and continue, and see what happens in Python Tutor:

[9]:
i = 1
while i < 5:
    print('Counted', i)
    if i > 3:
        print('break! Exiting the cycle!')
        break
        print('After the break')
    i += 1
    if i % 2 == 1:
        print('continue, jumping to next condition check')
        continue
        print('After the continue')
    print('arrived till the end')

print('Loop is over !')

jupman.pytut()
Counted 1
arrived till the end
Counted 2
continue, jumping to next condition check
Counted 3
arrived till the end
Counted 4
break! Exiting the cycle!
Loop is over !
[9]:

Questions about break and continue

QUESTION: Look at the following code fragments , and for each try guessing the result it produces (or if it gives an error):

  1. i = 1
    while i < 4:
        print('Counted', i)
        i += 1
        continue
    
    print('Loop is over !')
    
  2. i = 1
    while i < 4:
        print('Counted', i)
        continue
        i += 1
    
    print('Loop is over !')
    
  3. i = 3
    while i > 0:
        print('Counted', i)
        if i == 2:
            print('continue, jumping to condition check')
            continue
        i -= 1
        print('arrived till the end')
    
    print('Loop is over !')
    
  4. i = 0
    while True:
        i += 1
        print(i)
        if i > 3:
            break
    
    print('BONG')
    
  5. i = 0
    while True:
        if i < 3:
            continue
        else:
            break
        i += 1
    
    print('ZONG')
    
  6. i = 0
    while True:
        i += 1
        if i < 3:
            continue
        else:
            break
    
    print('ZANG')
    

Questions - Are they equivalent?

Look at the following code fragments: each contains two parts, A and B. For each value of the variables they depend on, try guessing whether part A will print exactly the same result printed by code in part B

  • FIRST think about the answer and write down the expected output

  • THEN try executing with each of the values of suggested variables

Are they equivalent? - BORG

print('A:')
while True:
    print('BORG')
    break

print('\nB:')
while False:
    pass
print('BORG')

Are they equivalent? - until 3

print('A:')
x = 0
while x < 3:
    print(x)
    x += 1

print('\nB:')
x = 1
while x <= 3:
    print(x-1)
    x += 1

Are they equivalent? - by chance

Remember randint(a, b) gives back a random integer N such that a <= N <= b

print('A:')
x = 0
while x < 3:
    x += 1
print(x)

print('\nB:')
x = 0
import random
while x != 3:
    x = random.randint(1,5)
print(x)

Are they equivalent? - until six

print('A:')
i = 0
while i < 3:
    print(i)
    i += 1
while i < 6:
    print(i)
    i += 1

print('\nB:')
i = 0
while i < 6:
    print(i)
    i += 1

Are they equivalent? - countdown 1

print('A:')
i = 2
print(i)
while i > 0:
    i -= 1
    print(i)

print('\nB:')
i = 2
while i > 0:
    print(i)
    i -= 1

Are they equivalent? - countdown 2

print('A:')
i = 2
print(i)
while i > 0:
    i -= 1
    print(i)

print('\nB:')
i = 2
while i > 0:
    print(i)
    i -= 1
print(i)
[10]:
print('A:')
i = 2
print(i)
while i > 0:
    i -= 1
    print(i)

print('\nB:')
i = 2
while i > 0:
    print(i)
    i -= 1
print(i)
A:
2
1
0

B:
2
1
0

Are they equivalent? - sorcery

print('A:')
s = 'sorcery'
i = 0
while s[i] != 'e':
    i += 1
print(s[i:])

print('B:')
s = 'sorcery'
i = len(s)
while s[i] != 'e':
    i -= 1
print(s[i:])

Are they equivalent? - ping pong

print('A:')
ping,pong = 0,3
while ping < 3 or pong > 0:
    print(ping,pong)
    ping += 1
    pong -= 1

print('\nB:')
ping,pong = 0,3
while not(ping >= 3 and pong <= 0):
    print(ping,pong)
    ping += 1
    pong -= 1

Are they equivalent? - zanna

print('A:')
n,i,s = 0,0,'zanna'
while i < len(s):
    if s[i] == 'n':
        n += 1
    i += 1
print(n)

print('\nB:')
n,i,s = 0,0,'zanna'
while i < len(s):
    i += 1
    if s[i-1] == 'n':
        n += 1
print(n)

Are they equivalent? - pasticcio

print('A:')
c,i,s = 0,0,'pasticcio'
while i < len(s):
    if s[i] == 'c':
        c += 1
    i += 1
print(c)

print('\nB:')
no,k,s = 0,0,'pasticcio'
while k < len(s):
    if s[k] != 'c':
        no += 1
    else:
        k += 1
print(len(s) - no)

Exercises - counters

Exercise - don’t break 1

✪ Look at the following code, and write in the following cell some code which produces the same result with a while and without using break

[11]:
x = 3
while True:
    print(x)
    if x == 0:
        break
    x -= 1
3
2
1
0
Show solution
[12]:

x = 3

# write here


Exercise - don’t break 2

✪ Look at the following code, and write in the following cell some code which produces the same result with a while and without using break

[13]:
la = [2,3,7,5,6]
k = 7   # 2 3 7
#k = 5  # 2 3 7 5 6
#k = 13 # 2 3 7 5 6

i = 0
while True:
    print(la[i])
    if i >= len(la)-1 or la[i] == k:
        break
    else:
        i += 1
2
3
7
Show solution
[14]:

la = [2,3,7,5,6]
k = 7   # 2 3 7
#k = 6  # 2 3 7 5 6
#k = 13 # 2 3 7 5 6

i = 0

# write here


Exercise - Give me a break

✪ Look at the following code, and write in the next cell some code which produces the same result with a while this time using a break

[15]:

x,y = 1,5    # (1,5)  (2,4)
#x,y = 2,8   # (2, 8) (3, 7) (4, 6)

while x < y or x == 4:
    print((x,y))
    x += 1
    y -= 1
(1, 5)
(2, 4)
Show solution
[16]:

x,y = 1,5    # (1,5)  (2,4)
#x,y = 2,8   # (2, 8) (3, 7) (4, 6)

# write here


Exercise - paperboard

✪ Prints integer numbers from 0 to k INCLUDED using a while, and for each number prints to its side one among the strings 'PA', 'PER' and 'BOARD' alternating them

Ex - for k=8 prints

0 PA
1 PER
2 BOARD
3 PA
4 PER
5 BOARD
6 PA
7 PER
8 BOARD
Show solution
[17]:

k = 8

# write here


Exercise - until ten

✪ Given two numbers x and y, write some code with a while which prints and increments the numbers, stopping as soon as one of them reaches ten.

x,y = 5,7

after your code it must result:

5 7
6 8
7 9
8 10
Show solution
[18]:

x,y = 5,7
#x,y = 8,4

# write here


Exercise - cccc

✪ Write some code using a while which given a number y, prints y rows containing the character c as many times as the row number.

Example - given:

y = 4

Prints:

c
cc
ccc
cccc
Show solution
[19]:

y = 4

# write here


Exercise - converge

✪ Given two numbers x and k, using a while modify and print x until it reaches k included

  • NOTE: k can either be greater or lesser than x, you must handle both cases

Example 1 - given:

x,k = 3,5

prints:

3
4
5

Example 2 - given:

x,k = 6,2

prints:

6
5
4
3
2
Show solution
[20]:

x,k = 3,5    # 3 4 5
#x,k = 6,2   # 6 5 4 3 2
#x,k = 4,4   # 4

# write here


Searching a sequence

We are at the airport, and we’ve been told to reach the gate of our trusted airline company Turbulenz. We don’t remember exactly the gate, but we know we have to stop at the first Turbulenz sign we find. If by mistake we went further, we might encounter other gates for international flights, and who knows where we would end up.

If we have to perform searches in potentially long sequences, and we don’t always need a complete visit, using a while loop is more convenient and efficient than a for.

We could represent the example above as a list:

[21]:
          #    0         1            2            3           4           5         6           7
airport = ['Flyall','PiercedWings','PigeonJet','Turbolenz', 'BoingBoing','Jettons','Turbulenz','BoingBoing']

Once the element is found, we would like the program to print the position in which it was found, in this case 3.

Naturally, if you read well the list search methods you already know there is a handy method .index('Turbulenz'), but in this notebook we adopt the philosophy of ‘do it yourself’, and will try building our search algorithms from scratch.

QUESTION: Can you think of some corner case where index method can also bring a problem?

Show answer

What we need

To build our search, we will need:

  1. control variable

  2. stop condition

  3. control variable update

The control variable in this case could be an index, the stop condition could evaluate whether we reached the end of the airport, and inside the cycle we will update the index to keep searching. But where should we evaluate whether or not we have found the gate? Furthermore, since we are expert programmers we believe in misfortune, and know horror scenarios could happen indeed, like Turbulenz company going bankrupt the very same day of our arrival! Thus, we also need to foresee the case our search may give no result, and decide what should happen in such situation.

How to check

There are two ways to check a discovery:

  1. most direct way is to place an exit check inside the body of while itself: we could put an if statement which controls when the element is found, and in such case performs the execution of a break command. It is by no means elegant, yet it could be a first approach.

  2. a better option would be performing the check in the boolean condition of the while, but devising a program which works in all cases could be slightly trickier.

Let’s try them both in the following exercises.

Exercise - Turbolenz with a break

✪✪ Write some code which uses a while to search the list airport for the FIRST occurrence of company: as soon as it is found, stops searching and PRINTS the index where it was found.

  • If the company is not found, PRINTS ‘Not found’

  • USE a break to stop the search

  • REMEMBER to test your code with all the suggested airports

Example 1 - given:

company='Turbolenz'
airport = ['Flyall','PiercedWings','PigeonJet','Turbolenz', 'BoingBoing','Jettons','Turbulenz','BoingBoing']

after your code, it must print:

Found the first Turbolenz at index 3

Example 2 - given:

company = 'FlapFlap'
airport = ['PiercedWings','BoingBoing','Turbolenz','PigeonJet']

it must print:

FlapFlap was not found
Show solution
[22]:



company = 'Turbolenz'
airport = ['Flyall','PiercedWings','PigeonJet','Turbolenz', 'BoingBoing','Jettons','Turbulenz','BoingBoing']

#company = 'FlapFlap'
#airport = ['PiercedWings','BoingBoing','Turbolenz','PigeonJet']
#airport = []
#airport = ['FlapFlap']
#airport = ['Turbolenz', 'FlapFlap']

# write here


Exercise - Turbulenz without break

✪✪ Try now to rewrite the previous program without using break nor continue: to verify the finding, you will need to enrich the termination condition.

Show solution
[23]:


company = 'Turbolenz'
airport = ['Flyall','PiercedWings','PigeonJet','Turbolenz', 'BoingBoing','Jettons','Turbulenz','BoingBoing']

#company = 'FlapFlap'
airport = ['PiercedWings','BoingBoing','Turbolenz','PigeonJet']
#airport = []
#airport = ['FlapFlap']
#airport = ['Turbolenz', 'FlapFlap']

# write here


QUESTION: you probably used two conditions in the while. By exchanging the order of the conditions in the proposed solution, would the program work fine? If not, in which cases could it fail?

Show answer

Exercise - hangar

✪✪ Our plane just landed but now it must reach the hangar, dodging all the extraneous objects on the track!

Write some code which given a string track with a certain number of non-alphanumeric characters at the beginning, PRINTS the word which follows these characters.

Example - given:

track = '★★🏳🏳♦🏳♦🏳🏳hangar★★★'

your code must print:

hangar★★★

  • YOU CAN’T know beforehand which extra characters you will find in the string

  • DO NOT write characters like ★🏳♦-_ in the code

HINT: to determine if you have found alphanumerical characters or numbers, use .isalpha() and .isdigit() methods

Show solution
[24]:

track = '★★🏳🏳♦🏳♦🏳🏳hangar★★★'   # hangar★★★
#track = '🏳🏳twinengine'         # twinengine
#track = '-★♦--♦--747-🏳'        # 747-🏳
#track = 'glider'               # glider
#track = '__♦__🏳__♦_'           # prints nothing

# write here


Exercise - Wild West

✪✪ The two outlaws Carson and Butch agreed to bury a treasure in the jolly town of Tombstone, ma now each of them wants to take back the treasure without sharing anything with the partner.

  • there is a road from Santa Fe until Tombstone to arrive to the treasure, which we represent as a list of strings

  • we use two indexes butch and carson to represent where the outlaws are on the road

  • each outlaw starts from a different town

  • at each turn Carson moves of one city

  • at each turn Butch moves of two cities, because he has a fast Mustang horse

Write some code which prints the run and terminates as soon as one them arrives to the last city, telling who got the treasure.

  • In the case both outlaws arrive to the last city at the same time, prints Final duel in Tombstone!

  • your code must work for any road and initial position carson and butch

Example - 1 given:

#             0         1          2           3            4               5
road = ['Santa Fe','Denver','Dodge City', 'Silverton', 'Agua Caliente', 'Tombstone']
carson,butch  = 3, 0

it must print:

Carson starts from Silverton
Butch starts from Santa Fe
Carson reaches Agua Caliente
Butch reaches Dodge City
Carson reaches Tombstone
Butch reaches Agua Caliente

Carson takes the treasure in  Tombstone !

Example 2 - given:

#            0         1          2           3              4               5
road = ['Santa Fe','Denver','Dodge City', 'Silverton', 'Agua Caliente', 'Tombstone']
carson,butch  = 3, 2

it must print:

Carson starts from Silverton
Butch starts from Dodge City
Carson reaches Agua Caliente
Butch reaches Agua Caliente
Carson reaches Tombstone
Butch reaches Tombstone

Final duel in  Tombstone !
Show solution
[25]:

#           0          1         2              3              4              5
road = ['Santa Fe','Denver','Dodge City', 'Silverton', 'Agua Caliente', 'Tombstone']

carson,butch  = 3, 0    #  Carson takes the treasure in Tombstone !
#carson,butch  = 0, 0   #  Butch takes the treasure in Tombstone !
#carson,butch  = 3, 2   #  Final duel in Tombstone !

# write here


Exercise - The Balance of Language

✪✪ In the sacred writings of Zamfir the Prophet, it is predicted that when all Earth inhabitants speak a language with all the words of same length, universal harmony will be reached among human people. This event is probably far in time and by that epoch the vocabulary of humans will be so wide and varied that checking all the words will certainly require powerful calculations: you are asked to program the underwater servers of Atlantis to perform a check in the centuries to come.

Given a string of words language, write some code which prints True if all the words have the same length, False otherwise.

To have an efficient algorithm, you must use a while:

  • stop the loop as soon you can determine with certainty the program result

  • DO NOT use break nor continue

Show solution
[26]:


language =  "eternal harmony forever"  # True
#language = "war and violence"      # False
#language = "vi rt uo si ty"        # True
#language = "deceit bullying"       # False
#language = "harmony crashed today" # False
#language = "peace"                 # True
#language = ""                      # True

# write here


Exercise - the tree shaker

✪✪✪ Giustino the farmer decides to radically improve his farm productivity with high-tech devices, and asks you to develop a ‘tree shaker’ (so he calls it..) to perturbate the trees and harvest the exotic fruits he planted in his highlands (thanks to climate change…)

The plantation is a sequence of fruit trees, elements of the landscape (stones, gravel, etc) and signs S. The beginning and end of a subsequence of trees is always marked by a sign.

The vehicle to design has a cargo_bed of capacity 7 where it can store the harvest.

Write some code to scan the plantation and harvests in cargo_bed the fruits as they are found.

  • USE a while, stopping as soon as the cargo_bed is full

  • DO NOT use break nor continue

  • DO NOT write fruit names or landscape elements (no bananas nor rocks ..). You can still write 'S', though.

Example - given:

[27]:
plantation=['rocks','stones',  'S',   'bananas','oranges','mangos','S',      'sand',    'stones','stones',
            'S',    'avocados','S',   'weeds',  'S',      'kiwi',  'mangos', 'S',       'S',     'S',
            'rocks','S',       'lime','S',      'pebbles','S',     'oranges','coconuts','S',     'gravel']

after your code, it must result:

>>> print(cargo_bed)
['bananas', 'oranges', 'mangos', 'avocados', 'kiwi', 'mangos', 'lime']
Show solution
[28]:
          #  0       1          2      3          4        5        6         7         8        9
plantation=['rocks','stones',  'S',   'bananas','oranges','mangos','S',      'sand',   'stones','stones',
           # 10      11         12     13        14        15       16        17        18       19
            'S',    'avocados','S',   'weeds',  'S',      'kiwi',  'mangos', 'S',      'S',     'S',
           # 20      21         22     23        24        25       26        27        28       29
            'rocks','S',       'lime','S',      'pebbles','S',     'oranges','coconuts','S',    'gravel']

#plantation = ['S','S']   # []
#plantation = ['S','lemons','S']   # ['lemons']
#plantation = ['sand','S','lemons','S']   # ['lemons']
#plantation = ['oranges']   # []
#plantation = ['S','1','2','3','4','5','6','7', '8','S']   # ['1','2','3','4','5','6','7']
#plantation = ['S','1','2','S','x','S','3','4','5','6','7','8','S','9']   # ['1','2','3','4','5','6','7']


cargo_bed = []

# write here


['bananas', 'oranges', 'mangos', 'avocados', 'kiwi', 'mangos', 'lime']

Exercise - the ghost castle

✪✪✪ Given a string and two characters char1 and char2, write some code which PRINTS True if all occurrences of char1 in string are always followed by char2

Example - given:

string,char1,char2 = 'fantastic story of the ghost castle', 's','t'

prints True because all the occurrences of s are followed by t

string,char1,char2 = "enthusiastic dadaist", 's','t'

prints False, because the sequence si is found, where s is not followed by t

  • USE a while, try to make it efficient by stopping as soon as possible.

  • DO NOT use break

  • DO NOT use any search method (no index, find, replace, count…)

Show solution
[29]:

string,char1,char2 = 'fantastic story of the ghost castle', 's','t' # True
#string,char1,char2 = "enthusiastic dadaist", 's','t'    # False
#string,char1,char2 = "beetroots", 'r','o'           # True
#string,char1,char2 = "beetroots", 'e','o'           # False
#string,char1,char2 = "a", 'a','b'                      # False
#string,char1,char2 = "ab", 'a','b'                     # True
#string,char1,char2 = "aa", 'a','b'                     # False


# write here


Modifying sequences

In the tutorial on for loops we’ve seen an important warning we repeat here:

X COMMANDMENT: You shall never ever add or remove elements from a sequence you are iterating with a for !

Falling into such temptations would produce totally unpredictable behaviours (do you know the expression pulling the rug out from under your feet ? )

If you really need to remove elements from a sequence you are iterating, use a while cycle or duplicate first a copy of the original sequence.

Note the advice is only about for cycles. In case of necessity, at the end suggests to adopt while loops. Let’s see when and how ot use them.

Stack - Drawing from a card deck

Suppose having a deck of cards which we represent as a list of strings, and we want to draw all the cards, reading them one by one.

We can write a while that as long as the deck contains cards, keeps removing cards from the top with the pop method and prints their name. Remember pop MODIFIES the list by removing the last element AND gives back the element as call result, which we can save in a variable we will call card:

[30]:
deck = ['3 hearts',   # <---- bottom
        '2 spades',
        '9 hearts',
        '5 diamonds',
        '8 clubs']    # <---- top

while len(deck) > 0:
    card = deck.pop()
    print('Drawn', card)

print('No more cards!')

jupman.pytut()
Drawn 8 clubs
Drawn 5 diamonds
Drawn 9 hearts
Drawn 2 spades
Drawn 3 hearts
No more cards!
[30]:

Looking at the code, we can notice that:

  1. the variable deck is initialized

  2. we verify that deck dimension is greater than zero

  3. at each step the list deck is MODIFIED by reducing its dimension

  4. it returns to step 2

The first three points are the conditions which guarantee the while loop will sooner or later actually terminate.

Stack - Drawing until condition

Suppose now to continue drawing cards until we find a heart suit. The situation is more complicated, because now the cycle can terminate in two ways:

  1. we find hearts, and interrupt the search

  2. there aren’t heart cards, and the deck is exhausted

In any case, in the end we must tell the user a result. To do so, it’s convenient initializing card at the beginning like an empty string for handling the case when no hearts cards are found (or the deck is empty).

Let’s try a first implementation which uses an internal if to verify whether we have found hearts, and in that case exits with a break command.

  • Try executing the code by uncomment the second deck which has no hearts cards, and look at the different execution.

[31]:
deck = ['3 hearts','2 spades','9 hearts','5 diamonds','8 clubs']
#deck = ['8 spades','2 spades','5 diamonds','4 clubs']   # no hearts!

card = ''
while len(deck) > 0:
    card = deck.pop()
    print('Drawn', card)
    if 'hearts' in card:
        break

if 'hearts' in card:
    print('Found hearts!')
else:
    print("Didn't find hearts!")

jupman.pytut()
Drawn 8 clubs
Drawn 5 diamonds
Drawn 9 hearts
Found hearts!
[31]:

Exercise - Don’t break my heart

✪ Write some code which solves the same previous problem:

  • this time DO NOT use break

  • ensure the code works with a deck without hearts, and also with an empty deck

  • HINT: put a multiple condition in the while

Show solution
[32]:

deck = ['3 hearts','2 spades','9 hearts','5 diamonds','8 clubs']
#deck = ['8 spades','2 spades','5 diamonds','4 clubs']   # no hearts!
#deck = []  #  no hearts !

card = ''

# write here


Questions - what happens?

QUESTION: Look at the following code fragments , and for each try guessing the result it produces (or if it gives an error):

  1. while []:
        print('z')
    print('BIG')
    
  2. while ['a']:
        print('z')
    print('BUG')
    
  3. la = []
    while len(la) < 3:
        la.append('x')
    print(la)
    
  4. la = ['x','y','z']
    while len(la) > 0:
        print(la.pop())
    
  5. la = ['x','y','z']
    while la:
        print(la.pop(0))
    
  6. la = [4,5,8,10]
    while la.pop() % 2 == 0:
        print(la)
    

Questions - are they equivalent?

Look at the following code fragments: each contains two parts, A and B. For each value of the variables they depend on, try guessing whether part A will print exactly the same result printed by code in part B

  • FIRST think about the answer

  • THEN try executing with each of the values of suggested variables

Are they equivalent? - train

print('A:')
la = ['t','r','a','i','n']
while len(la) > 0:
    print(la.pop())

print('\nB:')
la = ['t','r','a','i','n']
la.reverse()
while len(la) > 0:
    print(la.pop(0))

Are they equivalent? - append nx

print('A:')
x,n,la = 2,0,[]
while x not in la:
    la.append(n)
    n += 1
print(la)

print('\nB:')
x,la = 2,[]
while len(la) < 3:
    la.append(x)
    x += 1
print(la)

Exercises - stack

Exercise - break sum

✪ Look at the following code, and rewrite it in the following cell as while

  • this time use command break

[33]:
lst = []
i = 0
k = 10
while sum(lst) < k:
    lst.append(i)
    i += 1
    print(lst)
[0]
[0, 1]
[0, 1, 2]
[0, 1, 2, 3]
[0, 1, 2, 3, 4]
Show solution
[34]:

lst = []
i = 0

# write here


Exercise - travelbook

✪✪ Suppose you visited the attic and found a stack of books, which we represent as a list of strings. Each string is prefixed by a label of one character indicating the category (D for Detective story, T for Travel, H for History)

stack = ['H-Middle Ages',   # <---- bottom
         'T-Australia',
         'T-Scotland',
         'D-Suspects',
         'T-Caribbean']    # <---- top

Since we are passionate about travel books, we want to examine stack one book at a time to transfer books into another pile we call ŧravel, which at the beginning is empty. We start from the top book in stack, and transfer into travel only the books starting with the label T like ('T-Australia')

travel = []

Write some code that produces the following print:

At the beginning:
    stack:   ['H-Middle Ages', 'T-Australia', 'T-Scotland', 'D-Suspects', 'T-Caribbean']
    travel: []
Taken T-Caribbean
    stack:   ['H-Middle Ages', 'T-Australia', 'T-Scotland', 'D-Suspects']
    travel: ['T-Caribbean']
Discarded D-Suspects
    stack:   ['H-Middle Ages', 'T-Australia', 'T-Scotland']
    travel: ['T-Caribbean']
Taken T-Scotland
    stack:   ['H-Middle Ages', 'T-Australia']
    travel: ['T-Caribbean', 'T-Scotland']
Taken T-Australia
    stack:   ['H-Middle Ages']
    travel: ['T-Caribbean', 'T-Scotland', 'T-Australia']
Discarded H-Middle Ages
    stack:   []
    travel: ['T-Caribbean', 'T-Scotland', 'T-Australia']
  • The non-travel books are not interesting and must be discarded

  • Your code must work with any stack list

Show solution
[35]:

stack = ['H-Middle Ages',   # <---- bottom
         'T-Australia',
         'T-Scotland',
         'D-Suspects',
         'T-Caribbean']    # <---- top


travel = []

# write here


Exercise - BANG !

✪✪ There are two stacks of objects right_stack and left_stack which we represent as lists of strings. As a pastime, a cowboy decides to shoot the objects at the top of the stacks, alternating the stack at each shoot. The cowboy is skilled and always hits the target, so each shot decreases a stack.

  • Suppose the objects on top are the ones at the end of the list

  • To keep track of which stack to hit, use a variable shoot holding either 'R' or 'L' character

  • After each shot the cowboy if possible changes the stack , otherwise keeps shooting at the same stack until it’s empty.

  • your code must work for any stack and initial shot

Example - given:

left_stack = ['box','boot','horseshoe','bucket']
right_stack = ['bin','saddle','tin can']
shoot = 'R'

after your code, it must print:

Ready?
   left_stack: ['box', 'boot', 'horseshoe', 'bucket']
  right_stack: ['bin', 'saddle', 'tin can']
BANG! right:  tin can
   left_stack: ['box', 'boot', 'horseshoe', 'bucket']
  right_stack: ['bin', 'saddle']
BANG! left:   bucket
   left_stack: ['box', 'boot', 'horseshoe']
  right_stack: ['bin', 'saddle']
BANG! right:  saddle
   left_stack: ['box', 'boot', 'horseshoe']
  right_stack: ['bin']
BANG! left:   horseshoe
   left_stack: ['box', 'boot']
  right_stack: ['bin']
BANG! right:  bin
   left_stack: ['box', 'boot']
  right_stack: []
BANG! left:   boot
   left_stack: ['box']
  right_stack: []
Nothing to shoot on the right!
   left_stack: ['box']
  right_stack: []
BANG! left:   box
   left_stack: []
  right_stack: []
Show solution
[36]:

left_stack = ['box','boot','horseshoe','bucket']
right_stack = ['bin','saddle','tin can']
shoot = 'R'
#shoot = 'L'
#left_stack = ['bucket', 'box']

# write here


Exercise - Growing or degrowing?

✪✪ Write some code which given a list la, keeps MODIFYING the list according to this procedure:

  • if the last element is odd (i.e. 7), attaches a new number at the end of the list obtained by multiplying by two the last element (i.e. attaches 14)

  • if the last element is even, removes the last two elements

  • DO NOT create a new list (so no rows starting with la =)

  • WARNING: when we want both grow and degrow the sequence we are considering in a cycle, we must convince ourselves that sooner or later the termination condition will happen, it’s easy to make mistakes and end up with an infinite cycle!

  • HINT: to degrow the list, you can use the pop method

Example - given:

la = [3,5,6,7]

Executing the code, it must print:

 Odd: attaching 14
      la becomes [3, 5, 6, 7, 14]
Even: removing 14
      removing 7
      la becomes [3, 5, 6]
Even: removing 6
      removing 5
      la becomes [3]
 Odd: attaching 6
      la becomes [3, 6]
Even: removing 6
      removing 3
      la becomes []
Done! la is []
Show solution
[37]:

la = [3,5,6,7]

# write here


Continue

Go on with the challenges

[ ]: