user1358
user1358

Reputation: 643

Why nested list indexing gets swapped with list comprehensions?

I expected the following 2 code-segments to be essentially equivalent.

return tuple(tuple( False if (i,j) in neighborhood else avail[i][j]
    for i in range(len(avail)))     
    for j in range(len(avail[i])))

(False, False, True, True, True)
(False, False, True, True, True)
(False, False, True, True, True)
(False, False, True, True, True)
(True, True, True, True, True)

ls = [[val for val in row] for row in avail]
for i in range(len(avail)):
    for j in range(len(avail[i])):
        if (i,j) in neighborhood:
            ls[i][j] = False
return ls

[False, False, False, False, True]
[False, False, False, False, True]
[True, True, True, True, True]
[True, True, True, True, True]
[True, True, True, True, True]

The one with the for-loops is "correct" (thats what I wanted). Why did the list comprehension-version swap the indexes?

Upvotes: 1

Views: 77

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1121744

You have your loops inverted in the first version. You are creating inner tuples looping over range(len(avail)) and outer tuples with a loop over range(len(avail[i])).

Your code is the equivalent (with lists instead of tuples) of this instead:

outer = []
for j in range(len(avail[i])):
    inner = []
    for i in range(len(avail)):
        inner.append(False if (i,j) in neighborhood else avail[i][j])
    outer.append(inner)

and relied on i still being assigned as a global. You can see this too when you indent your expression based on the parenthesis:

return tuple(
    tuple(
        False if (i,j) in neighborhood else avail[i][j]
        for i in range(len(avail))
    )     
    for j in range(len(avail[i]))
)

Reverse the loops (indented differently to communicate the grouping better):

return tuple(
    tuple(False if (i,j) in neighborhood else avail[i][j] for j in range(len(avail[i])))     
    for i in range(len(avail)))

The above is the equivalent of:

outer = []
for i in range(len(avail)):
    inner = []
    for j in range(len(avail[i])):
        inner.append(False if (i,j) in neighborhood else avail[i][j])
    outer.append(inner)

You can simplify your code with using enumerate():

return tuple(
    tuple(False if (i,j) in neighborhood else v for j, v in enumerate(row))    
    for i, row in enumerate(avail))

Upvotes: 1

Related Questions