We have a square matrix of fixed size, and we receive a bunch of commands in input to operate on it. Two of them are about setting values on it, the other two perform a query and return an integer result.
This problem is quite simple, more detail on
CodeEval #87, still, I'm writing a few lines about it because it gave me a way to exercise with a couple of Python features.
Mapping command names with function names
We receive the description of what to do as a string like this "SetCol 32 20". The first element is the name of the command we want to perform on our matrix. It looks like it has been written thinking in a Pascal-based language, but I want to use Python, so I convert it in a standard compliant name using a dictionary:
ID_2_NAME = {'SetRow': 'set_row', 'SetCol': 'set_col', 'QueryRow': 'query_row', 'QueryCol': 'query_col'}
A couple of notation here. Firstly, as usually happen in these kind of problem, we don't have to care about error handling. In real life, we should expect bad data in input.
Secondly, I have decided to wrap matrix and related functionality in a class. Using free functions instead, I would have mapped the names with the functions, something like this:
SIZE = 256
board = # ...
def set_row(i, value):
for j in range(SIZE):
board[i][j] = value
# ...
ID_2_FUN = {'SetRow': set_row, # ...
# call set_row(15, 7)
command = 'SetRow'
ID_2_FUN[command](15, 7)
The use of a class led me to follow a different path. But this way was interesting, too.
Defining the Board class
The Board initializer just sets the size and the bidimensional list used as matrix:
def __init__(self, size=256): # 1
self.size = size
self.board = [[0 for _ in range(size)] for _ in range(size)] # 2
1. I decided not to fix the size, as I would have done in a more hasty implementation, but just to default it to the dimension, as required by CodeEval.
2. The matrix is built up on the fly with a double list comprehension. The for loop variables are not used, so I named them both with the idiomatic underscore. The external, right side, for-loop creates all the rows. The internal, left side, for-loop creates a list filled with zeroes and attach it to each row.
There is almost nothing to say about set_row() and set_col(), it's just a for-loop on the passed row or column to set each element to the specified value, see on GitHub if you want to compare your implementation with mine.
The two query functions are again logically equivalent, we just have to sum all the values for a specified row or column. However, in case of column, we have to perform an explicit for-loop, where for the row we could use the built-in sum() function:
def query_row(self, i):
return sum(self.board[i])
Calling the Board methods
Let see again a typical command line, "SetCol 32 20". We have to split it, so that we get as first element the command name, and then the function parameters. Once I all these elements, we could get help from getattr() to call the right method with its parameters:
board = Board()
# ...
command, *params = line.split() # 1
res = getattr(board, ID_2_NAME[command])(*[int(p) for p in params]) # 2
if res is not None: # 3
print(res)
1. We extract in the list params all the elements next to the first one. Beware, they are all strings here.
2. ID_2_NAME maps the command name, for instance SetCol, to the method name, set_col. Using getattr() we are actually calling the method on the passed object, same of board.set_col() here. On the right, I convert the parameters in params to integer and I extract them to single elements from the list, by the star operator.
3. The setters do not return anything. Or better, being Python functions, they return None. We are not interested in printing it. But we want to print what the query methods return.
The full Python script is on GitHub.
Go to the full post