Appendix G: Answers to Selected Exercises
This appendix provides worked solutions for selected exercises from Chapters 1 through 10. For Parts A and B, odd-numbered exercises are solved. For Parts C and D, selected exercises are included where the solution demonstrates an important pattern or technique. Solutions include explanations of reasoning, not just final answers.
Chapter 1: Welcome to Computer Science
A.1
Computer science is to programming as music theory is to playing an instrument. A musician can play notes without understanding harmony, chord progressions, or compositional structure — but understanding theory makes them a fundamentally better musician. It lets them improvise, compose, and understand why certain combinations sound good. Similarly, programming is the instrument, but computer science is the theory — it teaches you how to think about problems, evaluate solutions, and understand why certain approaches work better than others. You can write code without CS, but you won't know why your code is slow, how to design systems that scale, or how to solve problems you've never seen before.
A.3
"I understand the appeal of that idea, but consider this: AI tools generate code based on patterns in existing code — they don't understand the problem you're solving. Someone still needs to decompose the problem, evaluate whether the AI's output is correct, and design the overall system architecture. That 'someone' needs to understand CS fundamentals. It's like saying 'I don't need to understand math because I have a calculator.' The calculator helps, but you still need to know what to calculate and whether the answer makes sense."
A.5
- a) Chrome web browser — software (a program you install and run)
- b) Laptop's touchscreen — hardware (physical component)
- c) Wi-Fi adapter — hardware (physical component, even if it has firmware)
- d) Microsoft Word — software (application program)
- e) Computer's RAM — hardware (physical memory chip)
- f) An app on your phone — software (program running on the device)
B.1 (Example: Planning a road trip across three states)
Decomposition into sub-problems:
- Choose the route — which states, which highways, what order
- Estimate travel time — total driving hours, how to split across days
- Plan overnight stops — find hotels or campgrounds, make reservations
- Budget the trip — gas costs, food, lodging, activities
- Prepare the vehicle — oil change, tire pressure, emergency kit
- Pack for the trip — clothes, toiletries, entertainment, snacks
- Plan activities at each stop — what to see, restaurant reservations
Sub-problem 4 (Budget) can be further decomposed: - Estimate gas costs (distance / mpg * price per gallon) - Estimate food costs (meals per day * cost per meal * number of days) - Estimate lodging costs (cost per night * number of nights) - Add contingency buffer (10-20% of total)
B.5
Algorithm A (linear search): In the worst case, you'd check all 1,000 pages.
Algorithm B (binary search / "divide in half"): In the worst case: - Start with 1,000 pages. Check middle (page 500). Now searching 500 pages. - Check middle of remaining half (page 250 or 750). Now searching 250 pages. - Continue: 125 → 63 → 32 → 16 → 8 → 4 → 2 → 1
That's about 10 checks (since 2^10 = 1,024 ≈ 1,000).
Algorithm B is dramatically faster: ~10 checks vs. ~1,000 checks. This is the difference between O(n) and O(log n), which you'll formalize in Chapter 17 and implement in Chapter 19.
Chapter 2: Getting Started
A.1
REPL stands for Read-Eval-Print Loop: - Read: Python reads the expression you type - Eval: Python evaluates (executes) the expression - Print: Python prints the result to the screen - Loop: Python goes back and waits for your next input
It's an interactive cycle: type something, see the result immediately, type something else.
A.3
The terminal is the window/application that provides a text-based interface — it's the container. The shell is the program running inside the terminal that interprets your commands. The terminal is like a TV screen; the shell is like the channel you're watching. On macOS, Terminal.app is the terminal, and zsh is the default shell. On Windows, the Command Prompt window is the terminal, and cmd.exe is the shell. You can run different shells in the same terminal.
A.5
Every Python error message gives you three pieces of information: 1. The file name and line number where the error occurred 2. The type of error (e.g., SyntaxError, TypeError, NameError) 3. A description of what went wrong (e.g., "unexpected indent", "name 'pritn' is not defined")
B.1
print("CS", "is", "fun")
Output: CS is fun
By default, print() separates arguments with a single space.
B.3
print("one", end=" ")
print("two", end=" ")
print("three")
Output: one two three
The end=" " parameter replaces the default newline with a space, so all three prints appear on the same line. The third print uses the default end="\n", so the cursor moves to the next line after "three".
B.5
Assuming the user types Ada:
Hello Ada
Hello Ada
Both lines print Hello Ada, but the first uses print() with two separate arguments (separated by a space by default), while the second uses an f-string to embed name directly.
Chapter 3: Variables, Types, and Expressions
A.1
The box model says a variable is a container that holds a value. When you write x = 5, you're putting the value 5 into a box labeled x. The name tag model says a variable is a label (name tag) that points to an object in memory. When you write x = 5, you're attaching the label x to the integer object 5.
The models predict different behavior when you write:
a = [1, 2, 3]
b = a
b.append(4)
print(a) # [1, 2, 3, 4] — a changed!
Under the box model, b = a would copy the list into a new box. Changing b wouldn't affect a. But Python uses the name tag model: b = a makes both names point to the same list object. That's why changing through b also changes what a sees.
A.3
= is the assignment operator. It binds a name to a value:
score = 95 # "score" now refers to the integer 95
== is the equality comparison operator. It tests whether two values are equal and returns True or False:
score == 95 # True — "is score equal to 95?"
score == 100 # False — "is score equal to 100?"
A.5
- a)
my_variable— legal. Uses lowercase and underscores (good Python style). - b)
2nd_place— illegal. Variable names cannot start with a digit. - c)
_private— legal. Leading underscore is valid (conventionally means "private"). - d)
for— illegal.foris a reserved keyword in Python. - e)
student-name— illegal. Hyphens are not allowed (Python interprets it as subtraction:student - name). - f)
MAX_SIZE— legal. All caps with underscores is conventionally used for constants. - g)
class— illegal.classis a reserved keyword. - h)
camelCase— legal. Valid syntax, though Python convention preferssnake_case.
B.1
x = 10
y = x # y gets the value 10 (both point to the int 10)
x = 20 # x now points to 20; y still points to 10
z = x + y # z = 20 + 10 = 30
print(f"x = {x}, y = {y}, z = {z}")
Output: x = 20, y = 10, z = 30
Key insight: when we wrote y = x, y got the value x had at that moment (10). When we later changed x to 20, y was unaffected because integers are immutable — y keeps pointing to the original 10.
B.3
The bug is a TypeError. The input() function always returns a string, so temperature is the string "98.6", not the number 98.6. You can't subtract 32 from a string.
Fix:
temperature = float(input("Enter temperature in Fahrenheit: "))
celsius = (temperature - 32) * 5 / 9
print(f"{temperature}°F = {celsius:.1f}°C")
C.1
name = input("Enter your name: ")
birth_year = int(input("Enter your birth year: "))
age = 2026 - birth_year
print(f"{name}, you are approximately {age} years old.")
C.3
total_seconds = int(input("Enter total seconds: "))
hours = total_seconds // 3600
minutes = (total_seconds % 3600) // 60
seconds = total_seconds % 60
print(f"{total_seconds} seconds = {hours} hour(s), {minutes} minute(s), {seconds} second(s)")
The key operations:
- // 3600 gives full hours (integer division)
- % 3600 gives the leftover seconds after removing full hours
- // 60 on that remainder gives full minutes
- % 60 gives the final remaining seconds
C.5
bill = float(input("Enter the bill amount: "))
tip_percent = float(input("Enter tip percentage: "))
tip_amount = bill * tip_percent / 100
total = bill + tip_amount
print(f"Tip amount: ${tip_amount:.2f}")
print(f"Total: ${total:.2f}")
Chapter 4: Conditionals
A.1
x = 7
if x > 5: # True — 7 > 5
print("A") # prints "A"
if x > 10: # False — 7 is not > 10
print("B") # skipped
print("C") # always prints — not inside any if block
Output:
A
C
Note that these are two separate if statements, not an if-elif chain. Both are evaluated independently. "C" prints regardless because it's not indented under either if.
A.3
- a)
True and False→False(both must be true forand) - b)
True or False→True(at least one must be true foror) - c)
not True→False - d)
True and True and False→False(the lastFalsemakes the whole thing false) - e)
False or False or True→True(the lastTruemakes it true) - f)
not (True and False)→not False→True - g)
True or False and False→True or (False and False)→True or False→True(Note:andhas higher precedence thanor, soFalse and Falseis evaluated first) - h)
(True or False) and False→True and False→False(parentheses change the evaluation order)
A.5
The bug is using = (assignment) instead of == (comparison):
grade = input("Enter your grade (A/B/C/D/F): ")
if grade = "A": # SyntaxError! = is assignment, not comparison
print("Excellent!")
Fix: if grade == "A":
B.1
number = float(input("Enter a number: "))
if number > 0:
print(f"{number} is positive.")
elif number < 0:
print(f"{number} is negative.")
else:
print(f"{number} is zero.")
B.3
temp = float(input("Enter temperature in Fahrenheit: "))
if temp >= 90:
print("It's hot! Wear light clothing and stay hydrated.")
elif temp >= 70:
print("Nice weather! A t-shirt should be fine.")
elif temp >= 50:
print("It's cool. Grab a jacket.")
elif temp >= 32:
print("It's cold! Wear a warm coat.")
else:
print("Freezing! Bundle up — hat, gloves, heavy coat.")
Note the elif chain tests from highest to lowest. The order matters — since we check >= 90 first, by the time we reach >= 70, we know the temperature is between 70 and 89.
B.5
score = float(input("Enter your score (0-100): "))
if score < 0 or score > 100:
print("Error: score must be between 0 and 100.")
elif score >= 90:
print("A")
elif score >= 80:
print("B")
elif score >= 70:
print("C")
elif score >= 60:
print("D")
else:
print("F")
Chapter 5: Loops
A.1
for i in range(1, 21):
print(i)
A.3
for i in range(10, 0, -1):
print(i)
print("Blastoff!")
range(10, 0, -1) produces 10, 9, 8, ..., 1 (starts at 10, goes down to but not including 0, stepping by -1).
A.5
n = int(input("Enter a positive integer: "))
for i in range(1, n + 1):
print(i * 5, end=" ")
print()
For n = 4, this prints: 5 10 15 20
B.1
count = 1
while count <= 10:
print(count)
count += 1
B.3
word_count = 0
while True:
word = input("Enter a word (or 'quit' to stop): ")
if word == "quit":
break
word_count += 1
print(f"You entered {word_count} words.")
This uses the sentinel loop pattern: loop forever, check for the exit condition inside the loop, and break when found.
B.5
number = int(input("Enter a positive integer: "))
original = number
digit_sum = 0
while number > 0:
digit = number % 10 # extract last digit
digit_sum += digit # add to sum
number = number // 10 # remove last digit
print(f"Sum of digits: {digit_sum}")
For input 12345: - Iteration 1: digit = 5, sum = 5, number = 1234 - Iteration 2: digit = 4, sum = 9, number = 123 - Iteration 3: digit = 3, sum = 12, number = 12 - Iteration 4: digit = 2, sum = 14, number = 1 - Iteration 5: digit = 1, sum = 15, number = 0 → loop ends
C.1
scores = []
for i in range(5):
score = float(input(f"Enter test score {i + 1}: "))
scores.append(score)
total = sum(scores)
average = total / len(scores)
highest = max(scores)
lowest = min(scores)
print(f"Total: {total}")
print(f"Average: {average:.2f}")
print(f"Highest: {highest}")
print(f"Lowest: {lowest}")
Note: This solution uses sum(), max(), and min() built-ins. Without them, using the accumulator pattern:
total = 0
highest = float('-inf')
lowest = float('inf')
for i in range(5):
score = float(input(f"Enter test score {i + 1}: "))
total += score
if score > highest:
highest = score
if score < lowest:
lowest = score
average = total / 5
print(f"Total: {total}")
print(f"Average: {average:.2f}")
print(f"Highest: {highest}")
print(f"Lowest: {lowest}")
C.3
sentence = input("Enter a sentence: ")
spaces = 0
for char in sentence:
if char == " ":
spaces += 1
word_count = spaces + 1
print(f"Word count: {word_count}")
This is a simplification — it assumes single spaces between words and no leading/trailing spaces. In Chapter 7, you'll learn len(sentence.split()), which handles these edge cases correctly.
D.1 (Coding solution for 5-wide, 4-tall rectangle)
for row in range(4):
for col in range(5):
print("*", end="")
print() # newline after each row
Output:
*****
*****
*****
*****
Chapter 6: Functions
A.1
def say_hello():
print("Hello, World!")
say_hello()
say_hello()
say_hello()
A.3
def square(n):
return n ** 2
# Store result in a variable
result = square(7)
print(result) # 49
# Use directly in a print statement
print(square(12)) # 144
A.5
def print_separator(char, length):
print(char * length)
print_separator("-", 30) # ------------------------------
print_separator("=", 20) # ====================
print_separator("*", 10) # **********
B.1
def create_greeting(name, greeting="Hello", punctuation="!"):
return f"{greeting}, {name}{punctuation}"
# Just a name (uses defaults)
print(create_greeting("Alice")) # Hello, Alice!
# Name and custom greeting
print(create_greeting("Bob", "Howdy")) # Howdy, Bob!
# All three arguments
print(create_greeting("Carol", "Hey", "...")) # Hey, Carol...
# Keyword arguments in different order
print(create_greeting(punctuation="?!", name="Dave", greeting="Yo")) # Yo, Dave?!
B.3
def find_min_max(numbers):
if not numbers:
return None, None
current_min = numbers[0]
current_max = numbers[0]
for num in numbers[1:]:
if num < current_min:
current_min = num
if num > current_max:
current_max = num
return current_min, current_max
lo, hi = find_min_max([34, 12, 89, 5, 67, 23])
print(f"Min: {lo}, Max: {hi}") # Min: 5, Max: 89
B.5
def repeat_string(text, n=2):
return " ".join([text] * n)
print(repeat_string("ha", 3)) # ha ha ha
print(repeat_string("hey")) # hey hey
print(repeat_string("yo", 5)) # yo yo yo yo yo
C.1
x = 10
def change_x():
x = 20 # creates a LOCAL variable x
print(f"Inside function: x = {x}") # prints 20
change_x() # Inside function: x = 20
print(f"Outside function: x = {x}") # Outside function: x = 10
The function creates a local variable x that shadows the global x. The global x is never modified. When the function ends, the local x is destroyed.
C.3
Rewritten without global:
def increment(count):
return count + 1
count = 0
count = increment(count) # pass current value, get back new value
count = increment(count)
print(count) # 2
This is the preferred approach: pass data in as parameters, get results back via return. Avoid global — it makes code harder to understand and debug.
Chapter 7: Strings
A.1
message = "Computational Thinking"
- a)
message[0]→'C'(first character) - b)
message[13]→'T'(the 'T' in "Thinking") - c)
message[-1]→'g'(last character) - d)
message[-8]→'T'(8th from the end — also the 'T' in "Thinking") - e)
len(message)→22(includes the space)
A.3
The code produces a TypeError because strings are immutable — you cannot change individual characters with assignment.
Fix 1: Build a new string with concatenation:
language = "Javon"
language = "P" + language[1:3] + "h" + language[4:]
print(language) # "Pathon" — wait, that's still wrong!
Actually, to get "Python" from "Javon" we need more surgery. A cleaner approach:
Fix 2: Just reassign:
language = "Python"
print(language)
The real lesson: since strings are immutable, you don't modify them in place — you create new strings.
B.1
def normalize_name(name):
parts = name.strip().split()
if len(parts) == 1:
return parts[0].capitalize()
first = parts[0].capitalize()
last = parts[-1].capitalize()
return f"{last}, {first}"
print(normalize_name(" alice CHEN ")) # Chen, Alice
print(normalize_name("BOB martinez")) # Martinez, Bob
B.3
def remove_punctuation(text):
punctuation = ".,!?;:'\"()-"
result = ""
for char in text:
if char not in punctuation:
result += char
return result
print(remove_punctuation("Hello, World!")) # "Hello World"
print(remove_punctuation("It's a test (really).")) # "Its a test really"
B.5
def initials(full_name):
parts = full_name.strip().split()
result = ""
for part in parts:
result += part[0].upper() + "."
return result
print(initials("Anika Patel")) # A.P.
print(initials("mary jane watson")) # M.J.W.
print(initials(" cher ")) # C.
C.1
def pig_latin(word):
vowels = "aeiouAEIOU"
if word[0] in vowels:
return word + "yay"
else:
return word[1:] + word[0] + "ay"
print(pig_latin("apple")) # appleyay
print(pig_latin("hello")) # ellohay
print(pig_latin("python")) # ythonpay
print(pig_latin("is")) # isyay
Chapter 8: Lists and Tuples
A.1
foods = ["pizza", "sushi", "tacos", "ramen", "curry"]
print(f"First: {foods[0]}") # pizza
print(f"Last: {foods[-1]}") # curry
print(f"Middle: {foods[2]}") # tacos
print(f"Reversed: {foods[::-1]}") # ['curry', 'ramen', 'tacos', 'sushi', 'pizza']
A.3
fruits = ["apple", "banana", "cherry", "date"]
print(len(fruits)) # 4
print(fruits[-2]) # "cherry"
print(fruits[1:3]) # ["banana", "cherry"]
print("banana" in fruits) # True
print("grape" not in fruits) # True
A.5
append() adds a single item to the end of the list. extend() adds all items from an iterable individually:
a = [1, 2, 3]
a.append(4) # a is now [1, 2, 3, 4]
a.append([5, 6]) # a is now [1, 2, 3, 4, [5, 6]] — nested list!
b = [1, 2, 3]
b.extend([4, 5, 6]) # b is now [1, 2, 3, 4, 5, 6] — flat list
If you append a list, it becomes a single element (a nested list). If you extend with a list, each element is added individually.
B.1
def stats(numbers):
minimum = numbers[0]
maximum = numbers[0]
total = 0
for num in numbers:
if num < minimum:
minimum = num
if num > maximum:
maximum = num
total += num
average = total / len(numbers)
return (minimum, maximum, average, len(numbers))
result = stats([23, 45, 12, 67, 34, 89, 56])
print(f"Min: {result[0]}, Max: {result[1]}, Average: {result[2]:.2f}, Count: {result[3]}")
# Min: 12, Max: 89, Average: 46.57, Count: 7
B.3
def find_all_indices(lst, target):
indices = []
for i, item in enumerate(lst):
if item == target:
indices.append(i)
return indices
print(find_all_indices([3, 1, 4, 1, 5, 1], 1)) # [1, 3, 5]
print(find_all_indices([3, 1, 4, 1, 5, 1], 9)) # []
B.5
def rotate_left(lst, n):
n = n % len(lst) # handle n >= len(lst)
return lst[n:] + lst[:n]
print(rotate_left([1, 2, 3, 4, 5], 2)) # [3, 4, 5, 1, 2]
print(rotate_left([1, 2, 3, 4, 5], 0)) # [1, 2, 3, 4, 5]
print(rotate_left([1, 2, 3, 4, 5], 5)) # [1, 2, 3, 4, 5]
The n % len(lst) handles edge cases where n equals or exceeds the list length.
C.1
# a) Cubes of 1 through 10
result_a = [x ** 3 for x in range(1, 11)]
# b) Lengths of words
result_b = [len(word) for word in ["hello", "world", "python"]]
# c) Multiples of 3 from 1 to 20
result_c = [n for n in range(1, 21) if n % 3 == 0]
D.1 (Selected — demonstrating tuple unpacking with books)
books = [
("Project Hail Mary", "Andy Weir", 2021, 476),
("Dune", "Frank Herbert", 1965, 412),
("The Hunger Games", "Suzanne Collins", 2008, 374),
("1984", "George Orwell", 1949, 328),
("Klara and the Sun", "Kazuo Ishiguro", 2021, 307),
]
# a) Books published after 2000
print("Published after 2000:")
for title, author, year, pages in books:
if year > 2000:
print(f" {title} ({year})")
# b) Book with most pages
longest = books[0]
for book in books[1:]:
if book[3] > longest[3]:
longest = book
print(f"Longest: {longest[0]} ({longest[3]} pages)")
# c) Average page count
total_pages = sum(pages for _, _, _, pages in books)
average_pages = total_pages / len(books)
print(f"Average pages: {average_pages:.0f}")
Chapter 9: Dictionaries and Sets
A.1
capitals = {
"France": "Paris",
"Japan": "Tokyo",
"Brazil": "Brasilia",
"Canada": "Ottawa",
"Egypt": "Cairo",
}
print(capitals["France"]) # Paris
print(capitals.get("Germany", "Unknown")) # Unknown
capitals["Australia"] = "Canberra" # add new entry
print(f"Total countries: {len(capitals)}") # 6
A.3
info = {"name": "Alice", "age": 30, "city": "Portland"}
a = "name" in info # True — checks keys, and "name" is a key
b = "Alice" in info # False — "Alice" is a value, not a key
c = 30 in info # False — 30 is a value, not a key
d = info.get("phone", "N/A") # "N/A" — key doesn't exist, returns default
e = len(info) # 3
Key insight: the in operator checks keys, not values. This is an O(1) operation because dictionaries use hashing.
B.1
scores = {
"Alice": [88, 92, 85, 91],
"Bob": [75, 80, 72, 68],
"Carol": [95, 98, 92, 97],
"Dave": [60, 55, 70, 65],
}
best_student = None
best_average = 0
for name, student_scores in scores.items():
avg = sum(student_scores) / len(student_scores)
print(f"{name}: {avg:.1f}")
if avg > best_average:
best_average = avg
best_student = name
if avg < 70:
print(f" ⚠ {name} is below 70")
print(f"\nHighest average: {best_student} ({best_average:.1f})")
B.3
def merge_inventories(inv1, inv2):
merged = dict(inv1) # start with a copy of inv1
for item, quantity in inv2.items():
merged[item] = merged.get(item, 0) + quantity
return merged
shop_a = {"sword": 3, "shield": 2, "potion": 10}
shop_b = {"shield": 1, "potion": 5, "bow": 4}
print(merge_inventories(shop_a, shop_b))
# {'sword': 3, 'shield': 3, 'potion': 15, 'bow': 4}
C.1
cubes = {n: n ** 3 for n in range(1, 11)}
print(cubes)
# {1: 1, 2: 8, 3: 27, 4: 64, 5: 125, 6: 216, 7: 343, 8: 512, 9: 729, 10: 1000}
D.1
cs_students = {"Alice", "Bob", "Carol", "Dave", "Eve"}
math_students = {"Bob", "Dave", "Frank", "Grace"}
physics_students = {"Carol", "Eve", "Frank", "Hank"}
# Who is in both CS and Math?
print(cs_students & math_students) # {'Bob', 'Dave'}
# Who is in CS but not Physics?
print(cs_students - physics_students) # {'Alice', 'Bob', 'Dave'}
# Who is in all three?
print(cs_students & math_students & physics_students) # set() — empty! No one is in all three.
# How many unique students total?
all_students = cs_students | math_students | physics_students
print(f"Unique students: {len(all_students)}") # 8
Chapter 10: File I/O
A.1
# Write the file
with open("hello.txt", "w") as f:
f.write("Hello, file system!\n")
f.write("This is my first file.\n")
# Read it back
with open("hello.txt", "r") as f:
content = f.read()
print(content)
A.3
First, create the test file:
with open("numbers.txt", "w") as f:
for num in [10, 20, 30, 40, 50]:
f.write(f"{num}\n")
Then read and sum:
total = 0
with open("numbers.txt", "r") as f:
for line in f:
total += int(line.strip())
print(f"Sum: {total}") # Sum: 150
A.5
def append_line(filename, line):
with open(filename, "a") as f:
f.write(line + "\n")
# Test: three calls, three lines
append_line("log.txt", "First entry")
append_line("log.txt", "Second entry")
append_line("log.txt", "Third entry")
# Verify
with open("log.txt", "r") as f:
print(f.read())
The key is using "a" (append mode) instead of "w" (write mode). Append mode adds to the end of the file; write mode would erase everything and start fresh.
B.1
def grep(filename, keyword):
matches = []
with open(filename, "r") as f:
for line in f:
if keyword.lower() in line.lower():
matches.append(line.strip())
return matches
D.1 (Selected — CSV reading and filtering)
First, create the test file:
import csv
students = [
{"name": "Alice", "major": "CS", "gpa": "3.9"},
{"name": "Bob", "major": "Math", "gpa": "3.2"},
{"name": "Carol", "major": "CS", "gpa": "3.7"},
{"name": "Dave", "major": "Biology", "gpa": "2.8"},
{"name": "Eve", "major": "Physics", "gpa": "3.6"},
{"name": "Frank", "major": "CS", "gpa": "3.1"},
]
with open("students.csv", "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=["name", "major", "gpa"])
writer.writeheader()
writer.writerows(students)
Then read and filter:
import csv
print("Students with GPA above 3.5:")
with open("students.csv", "r", newline="") as f:
reader = csv.DictReader(f)
for row in reader:
if float(row["gpa"]) > 3.5:
print(f" {row['name']} ({row['major']}): {row['gpa']}")
Output:
Students with GPA above 3.5:
Alice (CS): 3.9
Carol (CS): 3.7
Eve (Physics): 3.6
Solutions for Chapters 11-27 are not included in this appendix. Consult your instructor or the online resources for additional solutions. For the progressive project (TaskFlow CLI), complete checkpoint code is available in each chapter's code/ directory.