9.1.4. Looping and Sorting#

import sys
from pathlib import Path

# Find project root by looking for _config.yml
current = Path.cwd()
for parent in [current, *current.parents]:
    if (parent / '_config.yml').exists():
        project_root = parent
        break
else:
    project_root = Path.cwd().parent.parent

# Add project root to path
sys.path.insert(0, str(project_root))

# Import shared teaching helpers and cell magics
from shared import thinkpython, diagram, jupyturtle, structshape
from shared.download import download

9.1.4.1. Looping Through String Lists#

You can use a for statement to loop through the elements of a list.

fruits = ['apple', 'banana', 'cherry']

for fruit in fruits:
    print(fruit)
apple
banana
cherry

.split() returns a list of words, we can use for to loop through them.

s = 'We are programmed to receive'  ### lyric from the Eagles' 1976 hit song "Hotel California".

for word in s.split():
    print(word)
We
are
programmed
to
receive

Not that it’s useful, but a for loop over an empty list never runs the indented statements.

for x in []:
    print('This never happens.')
### EXERCISE: Looping Through String Lists
# Difficulty: Basic
words = ['apple', 'Banana', 'cherry', 'Date', 'elderberry']
# 1. Loop through the words and print each word in lowercase
# 2. Create a new list containing only words that start with a vowel (a, e, i, o, u)
### Your code starts here:



### Your code ends here.

Hide code cell source

# Solution
words = ['apple', 'Banana', 'cherry', 'Date', 'elderberry']

print("Words in lowercase:")
for word in words:
    print(word.lower())

vowels = ['a', 'e', 'i', 'o', 'u']
starts_with_vowel = []
for word in words:
    if word[0].lower() in vowels:
        starts_with_vowel.append(word)

print(f"\nWords starting with a vowel: {starts_with_vowel}")
Words in lowercase:
apple
banana
cherry
date
elderberry

Words starting with a vowel: ['apple', 'elderberry']

9.1.4.2. Sorting String Lists#

Python provides a built-in function called sorted that sorts the elements of a list and the .sort() method that does similarly.

  • sorted()

  • .join()

scramble = ['c', 'a', 'b']
sorted(scramble)
['a', 'b', 'c']

The original list is unchanged.

scramble
['c', 'a', 'b']

sorted works with any kind of sequence, not just strings or lists. So we can sort the letters in a string like this.

sorted('letters')
['e', 'e', 'l', 'r', 's', 't', 't']

The result is a list. To convert the list to a string, we can use join.

letters = ''.join(sorted('letters'))

With an empty string as the delimiter, the elements of the list are joined with nothing between them.

In lists, you have a .sort() method, which is not available in strings; it is list only.

%%expect AttributeError
letters.sort()
AttributeError: 'str' object has no attribute 'sort'
### EXERCISE: Sorting Lists
# Difficulty: Intermediate
scores = [85, 92, 78, 90, 88]
names = ['Charlie', 'Alice', 'Bob']
# 1. Sort the scores in descending order (highest first)
# 2. Sort the names alphabetically and join them with commas
### Your code starts here:



### Your code ends here.

Hide code cell source

# Solution
scores = [85, 92, 78, 90, 88]
names = ['Charlie', 'Alice', 'Bob']

sorted_scores = sorted(scores, reverse=True)
sorted_names = sorted(names)
names_joined = ", ".join(sorted_names)

print(f"Scores (descending): {sorted_scores}")
print(f"Names (alphabetically): {names_joined}")
Scores (descending): [92, 90, 88, 85, 78]
Names (alphabetically): Alice, Bob, Charlie

9.1.4.3. Docstrings#

A docstring is a string at the beginning of a function that explains the interface (“doc” is short for “documentation”). Here is an example:

def polyline(n, length, angle):
    """Draws line segments with the given length and angle between them.
    
    n: integer number of line segments
    length: length of the line segments
    angle: angle between segments (in degrees)
    """    
    for i in range(n):
        forward(length)
        left(angle)

By convention, docstrings are triple-quoted strings, also known as multiline strings because the triple quotes allow the string to span more than one line.

A docstring should:

  • Explain concisely what the function does, without getting into the details of how it works,

  • Explain what effect each parameter has on the behavior of the function, and

  • Indicate what type each parameter should be, if it is not obvious.

Writing this kind of documentation is an important part of interface design. A well-designed interface should be simple to explain; if you have a hard time explaining one of your functions, maybe the interface could be improved.

### EXERCISE: Writing a Docstring
# Difficulty: Challenge
# Write a function called area_rectangle(width, height).
# 1. Add type hints for parameters and return value.
# 2. Include a docstring describing parameters, return value, and one raised error.
# 3. Raise ValueError if width or height is negative.
# 4. Call the function with (3, 4) and print the result.
### Your code starts here:


### Your code ends here.

Hide code cell source

# Solution
def area_rectangle(width: float, height: float) -> float:
    """Return the area of a rectangle.

    width: non-negative numeric width of the rectangle
    height: non-negative numeric height of the rectangle
    returns: numeric area
    raises: ValueError if width or height is negative
    """
    if width < 0 or height < 0:
        raise ValueError('width and height must be non-negative')
    return width * height

print(area_rectangle(3, 4))
12