1

I have a function that is supposed to increment each element in the odd rows of a matrix by 5, and each element in the even rows by 10. I have written the code below:

def incrementRows(matrix):
    for i in matrix:
        print(matrix.index(i))

        if matrix.index(i) % 2 == 0:
            matrix[matrix.index(i)] = [x + 5 for x in matrix[matrix.index(i)]]
        else:
            matrix[matrix.index(i)] = [x + 10 for x in matrix[matrix.index(i)]]


    return matrix


matrix = [[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15]]

print(incrementRows(matrix))

The problem with this code, is that judging by the print(matrix.index(i)) statement in the function, the loop never passes the first item in the list. I cannot understand why. Below is the output:

0
0
0
[[16, 17, 18, 19, 20], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]]

If I take the If/else statements out of the function, then the loop will iterate through each item in the list properly.

def incrementRows(matrix):
    for i in matrix:
        print(matrix.index(i))

    return matrix


matrix = [[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15]]

print(incrementRows(matrix))

Result:

0
1
2
[[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]]

Can anybody tell me why the If/else statements in the function are preventing the loop from iterating through each item in the list?

Omar N
  • 1,720
  • 2
  • 21
  • 33
  • possible duplicate of [Modifying list while iterating](http://stackoverflow.com/questions/1637807/modifying-list-while-iterating) – aruisdante Mar 18 '15 at 02:22
  • `index()` returns the position of a value in a list, this doesn't _seem_ to be what you want. What happens if you replace `matrix.index(i)` with just `i`? – 101 Mar 18 '15 at 02:30
  • If you need `i` to be index of the matrix instead of the value at that index, you can use `enumerate`, e.g. `for i, row in enumerate(matrix): print(i, row)`. – 101 Mar 18 '15 at 02:32

4 Answers4

2

With your given data, you're changing the matrix at each step to ensure what was the next item in it is always found at index 0.

To check that, change

print(matrix.index(i))

to

print(i, matrix, matrix.index(i))

and you'll see the much more informative output:

[1, 2, 3, 4, 5] [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]] 0
[6, 7, 8, 9, 10] [[6, 7, 8, 9, 10], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]] 0
[11, 12, 13, 14, 15] [[11, 12, 13, 14, 15], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]] 0

See? Each time, you're changing the 0th index so it equals the next item in the matrix.

Looping on a list's items and then doing an .index of each item on the list, of course, will always find the first occurrence of a copy of each item -- besides wasting a lot of computation in any case (making a loop O(N squared) that should of course be O(N)) this will provoke truly weird results in cases like yours.

To get back to sanity, change the part:

def incrementRows(matrix):
    for i in matrix:
        print(matrix.index(i))

to, e.g

def incrementRows(matrix):
    for where in range(len(matrix)):
        print(where)

and use where throughout the rest wherever you're now re-computing (wasting ridiculous numbers of cycles) matrix.index(i).

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
1

The problem is that on the second iteration, the first and second items compare equal, so index just gives you the first one again.

I believe it's time for you to learn about enumerate(). Basically here i is just the index without having to scan the list for matches over and over.

def incrementRows(matrix):
    for i, item in enumerate(matrix):
        print(i)

        if i % 2 == 0:
            matrix[i] = [x + 5 for x in item]
        else:
            matrix[i] = [x + 10 for x in item]


    return matrix


matrix = [[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15]]

print(incrementRows(matrix))

The code is much easier to read and it works properly now

John La Rooy
  • 295,403
  • 53
  • 369
  • 502
  • A note, the OP's original code has their cases backwards since it's supposed to add 10 to *even* rows, which would be when `i%2==0`. – aruisdante Mar 18 '15 at 02:42
1

It'll be much more straight-forward here to use a list-comp, and base the modulus on the "row number" that you're iterating over inside a conditional expression to choose either to add 5 or 10, eg:

matrix = [[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15]]
result = [
    [n + 5 if rowno % 2 == 0 else n + 10 for n in row] 
    for rowno, row in enumerate(matrix)
]
# [[6, 7, 8, 9, 10], [16, 17, 18, 19, 20], [16, 17, 18, 19, 20]]
Jon Clements
  • 138,671
  • 33
  • 247
  • 280
  • A note, the OP's original code has their cases backwards since it's supposed to add 10 to *even* rows, which would be when `i%2==0`. – aruisdante Mar 18 '15 at 02:43
  • @aruisdante I was pondering that - I admit I went with the logic in the OPs code instead of the reading properly the previous sentence though... I'm sure the OP can manage to swap the numbers around easily enough :p – Jon Clements Mar 18 '15 at 02:45
0

First off, you're modifying the structure of a list while iterating over it. Don't do that.

But I think you do that because you don't realize what this does:

for i in matrix:
    print(matrix.index(i))

I don't know if you realize this, but i is going to be the element in the list at that loop in the iteration. An example:

matrix = [[1,2],[3,4],[5,6]]
for row in matrix:
    print(row)

Outputs:

[1,2]
[3,4]
[5,6]

And matrix.index(i) is going to result in the index of the 'value' i in the list matrix using a simple linear search.

If you want a "c-style" for-loop over an index, you'd do it like so:

for i in xrange(len(matrix)): # Use just range(len(matrix)) if python 3.X
    print(matrix[i])

However, python has a nifty method, enumerate, which will give us both the index and the value at a location in an iterator.

In which case your code would become the following:

def increment_rows(matrix):
    for i,row in enumerate(matrix):
        print(i,row)
        matrix[i] = [v + 5 for v in row] if i % 2 else [v + 10 for v in row]
    print(matrix)
    return matrix

Which will result in the output you want. Of course you can actually collapse this into a single one-liner list comp leveraging enumerate:

def increment_rows(matrix):
    return [([v + 5 for v in row] if i % 2 else [v + 10 for v in row]) for i,row in enumerate(matrix)]
Community
  • 1
  • 1
aruisdante
  • 8,875
  • 2
  • 30
  • 37
  • Actually, the OP is **not** modifying the **structure** -- he's modifying fixed-places **items** within the list, which is just fine. He's wrong in a completely different way -- the use (and over-use) of `matrix.index(i)`. – Alex Martelli Mar 18 '15 at 02:48