COMP1701-004

fall 2023

lec-17

The Blue Block Continues Its Inexorable Advance

Marc Schroeder is present in class today and is conducting observations as part of an approved research study. No audio or video recording is being conducted.

 

His fieldnotes may record general observations about the overall nature of classroom activities and discussions. However, these will not include personal or identifying details about individuals or their participation, except for those students who are participating in other aspects of the study.

 

If you have any questions or concerns, then please let me know, and I’ll direct you to the appropriate resource.

any questions about A4?

DO THIS THING

Create a function that takes in a string and returns a list of all the "short" words in that string. Consider any word with 3 letters or less "short". Choose a good name, and don't forget those type hints!

# for example

shorties = yerfunction("my dog has numerous busy fleas")

# shorties should be ["my", "dog", "has"]

REVIEW

let's talk about these  things today:

 modifying existing list elements

 adding/removing list elements​​​​​​​

 nested / 2D lists

 iterating through 2D lists

 a super-important list gotcha

modding existing list elements

lists : modding existing items

s = "string"

s[1] = "p"

print(s)

Remember this from lec-15?

Strings are immutable, which is fancytalk for unchangeable. 

💥

NO TOUCHY

lists : modding existing items

a_list = "My dog has fleas".split()

print(a_list)  # ['My', 'dog', 'has', 'fleas']

a_list[3] = "rutabagas"

print(a_list)  # ['My', 'dog', 'has', 'rutabagas']

Lists don't have that issue!

Lists are mutable, which is fancytalk for changeable. 

Let's practice mutating a list.
(It sounds way cooler than "changing".)

sound_measurements = [32, 40, 31, 58, 111]
num_measurements = len(sound_measurements)

i = 3
while (i > 1):
  sound_measurements[i] = sound_measurements[i + 1]
  i -= 1
  
for measurement in sound_measurements:
	print(measurement, "", end="")

🙋🏻‍♂️❓🙋🏻‍♀️What's sound_measurements[4]?

🙋🏻‍♂️❓🙋🏻‍♀️At what index can we find 40?

🙋🏻‍♂️❓🙋🏻‍♀️What is len(sound_measurements)?

🙋🏻‍♂️❓🙋🏻‍♀️
What does the above code print to the terminal?

Use Python Tutor after to reinforce.

We've now shown how to create a new list with items in it...

...and how to change existing items in a list...

...but how can we add and remove items themselves?

nummy_foods = ["pizza", "sushi", "nachos", "mac & cheese", "natto"]
nummy_foods[len(nummy_foods) - 1] = "som tum"

🙋🏻‍♂️❓🙋🏻‍♀️What's in our nummy_foods list now?

adding list elements

lists : adding/removing items

We can use the append method to add things to the end of a list

great_calgary_restaurants = [ ]

great_calgary_restaurants.append("River Café")

# Gimme some other great Calgary restaurants
# we'll use append to add them to the list to
# practice...and because I'm always on the
# lookout for good restaurants!

REPL Time

lists : adding/removing items

We can use the + operator to add a list to the end of a list

drinks = ["OJ", "milk", "water"]

drinks = drinks + ["pop", "age-appropriate bev"]

print(drinks)
# ['OJ', 'milk', 'water', 'pop', 'age-appropriate bev']
drinks = ["OJ", "milk", "water"]

drinks = drinks + "pseudowine" # Boomsville

print(drinks)

Careful!

lists : adding/removing items

We can use the insert method to say "go to this index and add this item"

"March Hare" "Dormouse" "Mad Hatter"
0 1 2
tea_party = ["March Hare", "Dormouse", "Mad Hatter"]
tea_party.insert(1, "Alice")
"March Hare" "Alice" "Dormouse" "Mad Hatter"
0 1 2 3

Alice butts in at index 1...

...and shoves everyone down

lists : adding/removing items

PREDICT

# Let's Python Tutor this one
# predict what the list will look like at each step

a = "True"
b = "True"
c = "False"

the_list = [a, b, c]

the_list.append(str(False))

the_list = the_list + ["Dunno"] + the_list

the_list.insert(0, c)

🙋🏻‍♂️❓🙋🏻‍♀️What's len(the_list[3])?

removing list elements

lists : adding/removing items

We can use the remove method to remove a specified thing from a list

39 12 31 12 92
0 1 2 3 4
house_nums.remove(12)

The first 12 is removed...

...and everyone "slides over" into the "gap"

39 31 12 92
0 1 2 3

QUESTION
What do you think would happen if you tried to remove something that wasn't there?

house_nums = [39, 12, 31, 12, 92]

lists : adding/removing items

We can use the del keyword to remove a thing at a specific index from a list

"TS" "AL" "NR" "DJ"
0 1 2 3
initials = ["TS", "AL", "NR", "DJ"]
del initials[0]
"AL" "NR" "DJ"
0 1 2

watch that syntax - it's weird!

The specified item is removed...

...and everyone moves over into the "gap"

🙋🏻‍♂️❓🙋🏻‍♀️

What do you think would happen if you tried to remove something at an invalid index?

lists : adding/removing items

PREDICT

# Let's use the debugger on this one.
# Predict what the list will look like at each step.

a = True
b = True
c = False

the_list = [a, b, c]

the_list.append(False)

the_list.insert(0, c)

the_list.remove(b)

the_list += [True, True]

del the_list[3]

the_list

lists : adding/removing items

There are many more useful list methods out there.

A good developer gets into the habit of browsing the docs for useful methods.

The Python Reference (The Right Way) does a nice job.

lists : lists of lists

Remember this?

112 -4 9
0 1 2

list of ints

0.2 1.0 -100.03 -4.81
0 1 2 3

list of floats

True True False True False
0 1 2 3 4

list of bools

"foo" "hi there"
0 1

list of strings

(ominous music plays)

["chicken", "beef"] ["mashed potato", "baked potato"] ["corn", "peas", "carrots"]
0 1 2

list of lists aka 2D list

If a list can hold anything...

...then couldn't it hold other lists?!?

lists : modding existing items

movies = [
    ["Mad Max: Fury Road", "The Avengers", "Baby Driver"],
    ["Hellraiser", "The Mist", "Oculus"],
    ["2001", "Aliens", "Silent Running"],
    ["Caddyshack", "Airplane!", "The Big Lebowski"]
]

🙋🏻‍♂️❓🙋🏻‍♀️
What statement would mutate the list so that Caddyshack became Groundhog Day?

🙋🏻‍♂️❓🙋🏻‍♀️
What code would turn the list with Hellraiser in it into an empty list?

Let's practice mutating a 2D list.

movies_as_text = "Mad Max: Fury Road,The Avengers,Baby Driver"

movies_in_genres = [
    movies_as_text.split(","),
  	["The Omen", "The Exorcist", "The Changeling"]
]

print(movies_in_genres[0])

print(movies_in_genres[1][0])

for movies in movies_in_genres:
  print(movies[1])

Give This a Try

What is printed to the console by this code?

iterating through 2D/nested lists

lists : nested fors

Many problems you deal with in the Land of Software involves data in some sort of tabular format

skinny;10;42;32 
lady bug;90;11;15 
rickNmorty;48;22;98
D00MZDAY;99;101;12

video game high scores

2 0 2 -5 -3 -2 -5 -5 -5 -2
-11 -4 -8 -4 -6 -14 -6 -8 -12 -4
-3 -3 -13 -15 -6 2 -14 -7 1 -10
0 9 4 -3 5 8 -3 2 0 7

temp ranges over 4 days

First Tube, 8:41
Gotta' Jibboo, 11:54
Foam, 9:09
The Lizards, 10:18
You Enjoy Myself, 20:57

live Phish song durations

G4|--|R2
G3|--|S2
W2|--|W1

dice arrangements

lists : nested fors

Such data can often nicely be represented using lists of lists

skinny;10;42;32 
lady bug;90;11;15 
rickNmorty;48;22;98
D00MZDAY;99;101;12

video game high scores

2 0 2 -5 -3 -2 -5 -5 -5 -2
-11 -4 -8 -4 -6 -14 -6 -8 -12 -4
-3 -3 -13 -15 -6 2 -14 -7 1 -10
0 9 4 -3 5 8 -3 2 0 7

temp ranges over 4 days

First Tube, 8:41
Gotta' Jibboo, 11:54
Foam, 9:09
The Lizards, 10:18
You Enjoy Myself, 20:57

live Phish song durations

G4|--|R2
G3|--|S2
W2|--|W1

dice arrangements

high_scores = [
    ["skinny", [10, 42, 32]],
    ["lady bug", [90, 11, 15]],
    ["rickNmorty", [48, 22, 98]],
    ["D00MZDAY", [99, 101, 12]]
]
temp_ranges = [
    [2, 0, 2, -5, -3, -2, -5, -5, -5, -2],
    [-11, -4, -8, -4, -6, -14, -6, -8, -12, -4],
    [-3, -3, -13, -15, -6, 2, -14, -7, 1, -10],
    [0, 9, 4, -3, 5, 8, -3, 2, 0, 7]
]
song_durations = [
    ["First Tube", [8, 41]],
    ["Gotta' Jibboo", [11, 54]],
    ["Foam", [9, 9]],
    ["The Lizards", [10, 18]],
    ["You Enjoy Myself", [20, 57]],
]
building = [
    ["G4", '', "R2"],
    ["G3", '', "S2"],
    ["W2", '', "W1"],
]

You don't have to follow the original data format - you can make choices that make your life easier!

QUESTION
How do you suppose you could create these lists if the original data was text?

lists : nested fors

We usually want to walk - or iterate - through all "inner" lists inside the "outer" list and do something

high_scores = [
    ["skinny", [10, 42, 32]],
    ["lady bug", [90, 11, 15]],
    ["rickNmorty", [48, 22, 98]],
    ["D00MZDAY", [99, 101, 12]]
]
temp_ranges = [
    [2, 0, 2, -5, -3, -2, -5, -5, -5, -2],
    [-11, -4, -8, -4, -6, -14, -6, -8, -12, -4],
    [-3, -3, -13, -15, -6, 2, -14, -7, 1, -10],
    [0, 9, 4, -3, 5, 8, -3, 2, 0, 7]
]
song_durations = [
    ["First Tube", [8, 41]],
    ["Gotta' Jibboo", [11, 54]],
    ["Foam", [9, 9]],
    ["The Lizards", [10, 18]],
    ["You Enjoy Myself", [20, 57]],
]
building = [
    ["G4", '', "R2"],
    ["G3", '', "S2"],
    ["W2", '', "W1"],
]

print the number of high scores under 40 for each player

find the average temperature across all days

find the glass score

find the shortest song longer than 10 minutes

lists : nested fors

This iteration is very often done using a nested for loop

high_scores = [
    ["skinny", [10, 42, 32]],
    ["lady bug", [90, 11, 15]],
    ["rickNmorty", [48, 22, 98]],
    ["D00MZDAY", [99, 101, 12]]
]

print the number of high scores under 40 for each player

pseudocode

for every player in the outer list

["skinny", [10, 42, 32]],

count the scores < 40 and print

need to loop through these scores and count

need to loop through each player's info

lists : nested fors

high_scores = [
    ["skinny", [10, 42, 32]],
    ["lady bug", [90, 11, 15]],
    ["rickNmorty", [48, 22, 98]],
    ["D00MZDAY", [99, 101, 12]]
]
for curr_player in high_scores:
    curr_player_name = curr_player[0]
    curr_player_scores = curr_player[1]

    under_40_count = 0
    
    for curr_score in curr_player_scores:
        if curr_score < 40:
            under_40_count += 1

    print(curr_player_name, ":", under_40_count)
skinny : 2
lady bug : 2
rickNmorty : 1
D00MZDAY : 1

A Solution

nesting hurts the brain...what could we do to lessen the ouch?

lists : nested fors

high_scores = [
    ["skinny", [10, 42, 32]],
    ["lady bug", [90, 11, 15]],
    ["rickNmorty", [48, 22, 98]],
    ["D00MZDAY", [99, 101, 12]]
]
def num_scores_under_40(scores: list) -> int:
    under_40_count = 0

    for curr_score in scores:
        if curr_score < 40:
            under_40_count += 1

    return under_40_count


for curr_player in high_scores:
    curr_player_name = curr_player[0]
    curr_player_scores = curr_player[1]

    under_40_count = num_scores_under_40(curr_player_scores)

    print(curr_player_name, ":", under_40_count)

Functions make things less hurty

It's a bit less painful - and you can test the function separately, which is a plus.

a big list gotcha

a big list gotcha

x = "foo"

y = x

print("1: x is", x)
print("2: y is", y)

x += "bar"
print("3: x is", x)
print("4: y is", y)

Predict what is printed

a big list gotcha

x = ["it's", "a", "monorail"]

y = x

print("1: x is", x)
print("2: y is", y)

del x[2]
x.append("trap!")

print("3: x is", x)
print("4: y is", y)

Predict what is printed

WHAT DARK WITCHERY IS THIS?!?!

a big list gotcha

x = ["it's", "a", "monorail"]

y = x

print("1: x is", x)
print("2: y is", y)

del x[2]
x.append("trap!")

print("3: x is", x)
print("4: y is", y)

Let's use Python Tutor

a big list gotcha

x = ["it's", "a", "monorail"]
loc_of_xs_list = id(x)

y = x
loc_of_ys_list = id(y)

print(f"1: the list x refers to is at {loc_of_xs_list}")
print(f"2: the list y refers to is at {loc_of_ys_list}")

del x[2]
x.append("trap!")

print("3: x is", x)
print("4: y is", y)

id() is a useful function to figure out "who" an object is

lec-17

By Jordan Pratt

lec-17

nested for | modifying lists | list return values | a list gotcha

  • 195