Comet wants FieldSim to point at the first rock before Pip rolls out. "Easy," she says. "The rock in the top right corner."
Wren taps the field drawing. "What does the evidence say? There are four rocks. Which one is first depends on how you read the grid."
"I read like a book," Comet says. "Left to right, top to bottom."
Nova lights up the field one row at a time, each row sweeping from left to right. "How can I help?" she asks. "That is row by row. Search each row in turn, and give back the first match."
Comet hands you the pencil. "Lead programmer, which rock comes first?"
To search a grid, take each row in turn and search along it. The function find_in_grid() gives back the (row, col) of the first match, or None.
FIELD is a list of strings. rows[r][c] reads one character, the same way as a cell in a list of lists. Made-up data from the FieldSim story.
FIELD = [
"..*..#",
".#....",
"...#*.",
"*.....",
"..#..*",
] # grid_find_try.py
from field_data import FIELD
def find_in_grid(rows, mark):
"""(row, col) of the first cell holding mark, row by row, or None."""
for r in range(len(rows)):
for c in range(len(rows[r])):
if rows[r][c] == mark:
return (r, c)
return None
print(find_in_grid(FIELD, "#"))
print(find_in_grid(FIELD, "*"))
print(find_in_grid(FIELD, "R")) (0, 5) (0, 2) None
Wren makes a copy of find_in_grid that also counts how many cells it checks. The copy has its own file name, so search work never changes the real function.
# grid_count.py
from field_data import FIELD
def find_count(rows, mark):
"""Gives back ((row, col) or None, number of checks)."""
checks = 0
for r in range(len(rows)):
for c in range(len(rows[r])):
checks += 1
if rows[r][c] == mark:
return (r, c), checks
return None, checks
print(find_count(FIELD, "#"))
print(find_count(FIELD, "*"))
print(find_count(FIELD, "R")) | Search | Cells checked, in order | Result |
|---|---|---|
| "#" | (0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5) | (0, 5) |
| "*" | (0, 0), (0, 1), (0, 2) | (0, 2) |
| "R" | all 30 cells, row by row | None |
((0, 5), 6) ((0, 2), 3) (None, 30)
"First" depends on the search order. This copy searches column by column, top to bottom in each column.
# col_first.py
from field_data import FIELD
def find_by_column(rows, mark):
"""(row, col) of the first cell holding mark, column by column."""
for c in range(len(rows[0])):
for r in range(len(rows)):
if rows[r][c] == mark:
return (r, c)
return None
print(find_by_column(FIELD, "#"))
print(find_by_column(FIELD, "*")) (1, 1) (3, 0)
# grid_oops.py
from field_data import FIELD
def find_in_grid(rows, mark):
"""(row, col) of the first cell holding mark, row by row, or None."""
for r in range(len(rows)):
for c in range(len(rows[r])):
if rows[r][c] == mark:
return (r, c)
return None
row, col = find_in_grid(FIELD, "R") Traceback (most recent call last):
File "grid_oops.py", line 14, in <module>
row, col = find_in_grid(FIELD, "R")
^^^^^^^^
TypeError: cannot unpack non-iterable NoneType object You searched a whole field one cell at a time. Tomorrow you put every search into one module, search.py.