Hi I was working with a matrix in python call it a
:
a = [
[0,0,0],
[0,0,0],
[0,0,0]
]
I would like to change the element on the second row in the first column (a[1][0]
) to 1 yielding the following result :
a = [
[0,0,0],
[1,0,0],
[0,0,0]
]
You can of course accomplish this easily with :
a[1][0] = 1
Unfortunately I'm a loony who would like to accomplish this purely functional :) The conditions being :
for in
are used.a
with a changed element is stored in a second variable b
without changing a
.The wished for result should look something like this :
a = [
[0,0,0],
[0,0,0],
[0,0,0]
]
b = someOperation(a)
assert a == [[0,0,0],[0,0,0],[0,0,0]]
assert b == [[0,0,0],[1,0,0],[0,0,0]]
# the above asserts should not trigger
Does anyone know a (purely functional) solution to my problem? Thanks in advance.
If you want the purely functional way of doing something like this, you may want to consider how to do it in Haskell - the gold standard of functional programming.
For small, ad-hoc problems, if you need to access elements by index, you'd typically zip your data sequence with a number generator:
ghci> zip [0..] "abc"
[(0,'a'),(1,'b'),(2,'c')]
You can do the same in Python:
from itertools import count
def index_list(lst):
return zip(count(), lst)
Using it on 'abc'
:
>>> list(index_list('abc'))
[(0, 'a'), (1, 'b'), (2, 'c')]
(I'm only using list
in the above example in order to show the result.)
Likewise, you can index a nested list, which includes your matrix:
def index_matrix(matrix):
return index_list(map(index_list, matrix))
You now have an map of a map of tuples, where the first element of the tuple is the row or column index, and the second element is the value that was indexed.
We can do the same in Haskell, with the OP's input:
ghci> fmap (fmap (zip [0..])) $ zip [0..] [[0,0,0],[0,0,0],[0,0,0]]
[(0,[(0,0),(1,0),(2,0)]),(1,[(0,0),(1,0),(2,0)]),(2,[(0,0),(1,0),(2,0)])]
The index_matrix
Python function produces conceptually the same shape of output, but made up from zip and map objects.
Since index_matrix
has indexed both rows and columns, you can now iterate through the map of maps and replace the value at a particular row and column:
def replace_in_matrix(matrix, row, col, value):
return map(lambda r:
map(lambda c:
value if r[0] == row and c[0] == col else c[1], r[1]), index_matrix(matrix))
Try it out on the OP matrix:
>>> m = [[0,0,0],[0,0,0],[0,0,0]]
>>> result = replace_in_matrix(m, 1, 0, 1)
>>> list(map(list, result))
[[0, 0, 0], [1, 0, 0], [0, 0, 0]]
>>> m
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
This also shows that m
remains unmodified.
The ad-hoc projection in Haskell is similar:
ghci> fmap (fmap snd)
$ fmap snd
$ fmap (\(i, rs) -> (i, fmap (\(j, x) -> (j, if i == 1 && j == 0 then 1 else x)) rs))
$ fmap (fmap (zip [0..]))
$ zip [0..] [[0,0,0],[0,0,0],[0,0,0]]
[[0,0,0],[1,0,0],[0,0,0]]
(I've inserted some line breaks for slightly improved readability.)