Python: short coordinate function unexpectedly changes incoming argument value
Question:
The goal here is to take some list of coordinates, like [[1,2],[3,4],[7,1]]
, then figure out how big the canvas should be if you want to print all these coords. Take the maximal bottom left coordinate and minimal upper right coordinate that will snugly fit a canvas to these points. In the above list, for example, we’re looking for [[1,1],[7,4]]
, which defines the smallest rectangle where all those points will fit.
In the middle of this function, I’m seeing the incoming “board” assigned a new value.
def print_board(board):
# import pdb; pdb.set_trace()
dimensions = None
for i in board:
if dimensions == None:
dimensions = [i, i]
else:
dimensions[0][0] = min(dimensions[0][0], i[0])
#'board' is redefined !!!
dimensions[0][1] = min(dimensions[0][1], i[1])
#dimensions[1][0] = max(dimensions[1][0], i[0])
#dimensions[1][1] = max(dimensions[1][1], i[1])
# (after we get the canvas size
# we print the canvas with the points on it
# but we never make it that far without an error)
As the for loop moves through the coordinates in the incoming board
, it seems to be setting board[0]
to whatever coordinate it’s looking at at the time. So [[1,2],[3,4],[7,1]]
will change first to [[3,4],[3,4],[7,1]]
, then to [[7,1],[3,4],[7,1]]
.
I wouldn’t expect board
to change at all.
(Python 3.2.2)
Answers:
When you do
dimensions = [i, i]
you’re setting both items in dimensions
to the first point in your board — not making copies of that point.
Then when you do
dimensions[0][0] = min(dimensions[0][0], i[0])
dimensions[0][1] = min(dimensions[0][1], i[1])
you’re updating that same point — the first point in your board — to the results of the min
functions.
Try something like this, instead:
def print_board(board):
xs, ys = zip(*board) # separate out the x and y coordinates
min_x, max_x = min(xs), max(xs) # find the mins and maxs
min_y, max_y = min(ys), max(ys)
dimensions = [[min_x, min_y], [max_x, max_y]] # make the dimensions array
As an extension of agfs answer, you can use numpy for even more efficient and succinct code:
import numpy as np
def print_board(board):
a = np.array(board)
return [a.min(axis=0).tolist(), a.max(axis=0).tolist()]
If your board is a numpy array already, and you let the function return a tuple of numpy arrays, it shortens even more:
def print_board(board):
return board.min(axis=0), board.max(axis=0)
The goal here is to take some list of coordinates, like [[1,2],[3,4],[7,1]]
, then figure out how big the canvas should be if you want to print all these coords. Take the maximal bottom left coordinate and minimal upper right coordinate that will snugly fit a canvas to these points. In the above list, for example, we’re looking for [[1,1],[7,4]]
, which defines the smallest rectangle where all those points will fit.
In the middle of this function, I’m seeing the incoming “board” assigned a new value.
def print_board(board):
# import pdb; pdb.set_trace()
dimensions = None
for i in board:
if dimensions == None:
dimensions = [i, i]
else:
dimensions[0][0] = min(dimensions[0][0], i[0])
#'board' is redefined !!!
dimensions[0][1] = min(dimensions[0][1], i[1])
#dimensions[1][0] = max(dimensions[1][0], i[0])
#dimensions[1][1] = max(dimensions[1][1], i[1])
# (after we get the canvas size
# we print the canvas with the points on it
# but we never make it that far without an error)
As the for loop moves through the coordinates in the incoming board
, it seems to be setting board[0]
to whatever coordinate it’s looking at at the time. So [[1,2],[3,4],[7,1]]
will change first to [[3,4],[3,4],[7,1]]
, then to [[7,1],[3,4],[7,1]]
.
I wouldn’t expect board
to change at all.
(Python 3.2.2)
When you do
dimensions = [i, i]
you’re setting both items in dimensions
to the first point in your board — not making copies of that point.
Then when you do
dimensions[0][0] = min(dimensions[0][0], i[0])
dimensions[0][1] = min(dimensions[0][1], i[1])
you’re updating that same point — the first point in your board — to the results of the min
functions.
Try something like this, instead:
def print_board(board):
xs, ys = zip(*board) # separate out the x and y coordinates
min_x, max_x = min(xs), max(xs) # find the mins and maxs
min_y, max_y = min(ys), max(ys)
dimensions = [[min_x, min_y], [max_x, max_y]] # make the dimensions array
As an extension of agfs answer, you can use numpy for even more efficient and succinct code:
import numpy as np
def print_board(board):
a = np.array(board)
return [a.min(axis=0).tolist(), a.max(axis=0).tolist()]
If your board is a numpy array already, and you let the function return a tuple of numpy arrays, it shortens even more:
def print_board(board):
return board.min(axis=0), board.max(axis=0)