Skip to content

← Memory, Copies and Slow Code step 2 of 38

Easy Primitives

Fix a Function That Changes Its Caller's List

A caller passes you their list and keeps using it afterwards. If your function changes that list in place, the caller's data changes too, and the bug shows up far from your code. The three functions in the starter all return the right answer, and all three change the data you pass them.

tags = ["python", "typing"]
more = add_tag(tags, "async")
print(more)  # ['python', 'typing', 'async']
print(tags)  # ['python', 'typing', 'async'], but the caller still wants 2 tags

Run the starter first. The report shows each function's result next to the caller's data after the call.

What to write

Fix the three functions. Each one returns a new object and leaves its arguments exactly as they were.

  • add_tag(tags, tag) returns tags with tag added at the end.
  • sorted_tags(tags) returns the same tags in alphabetical order.
  • mark(board, row, col) returns a board with "X" at board[row][col]. A board is a list of rows, and each row is a list of strings. The caller's board, and every row in it, stays unchanged.

Why the starter is wrong

tags.append(tag) and tags.sort() change the caller's list itself. mark does copy the board with list(board), but that is a shallow copy. It makes a new outer list that holds the same row lists, so writing into a row of the copy writes into the caller's row.

Edge cases

  • Keep duplicates. add_tag(["a"], "a") returns ["a", "a"], and sorted_tags keeps every copy of a repeated tag.
  • An empty list is valid. add_tag([], "first") returns ["first"].
  • The cell may already hold "X" or "O". Put "X" there either way.
  • row and col are always inside the board.

What the driver reports

solve calls each function once and returns one entry per function. Keep it as it is.

  • returned is what the function returned.
  • caller_tags_after, caller_names_after and caller_board_after show the argument as the caller sees it after the call. Each must equal the input.