Skip to content Skip to sidebar Skip to footer

List Comprehension Behavior In Python

I am working with Codeskulptor on a rock collision problem. I want to check collisions between rocks and my rocks are in a list. I came up with the solution to build a list of comb

Solution 1:

First thing to do when understanding a list comprehension is to expand it to a regular set of for loops. Read the loops from left to right and nest accordingly.

Working code:

def combinations(items):
    n_items = []
    for n,item in enumerate(items):
        n_items.append((n,item))
    result = []
    for n, item in n_items:
        for m, item2 in n_items[n:]:
            if n != m:
                result.append((item, item2))
    return result

and your attempt that doesn't work:

def combinations2(items):
    result = []
    for n, item in enumerate(items):
        for m, item2 in enumerate(items[n:]):
            if n != m:
                result.append((item, item2))
    return result

Perhaps this way it is easier to see what goes wrong between the two versions.

Your version slices justitems, not the indices produced by enumerate(). The original version slices [(0, 'A'), (1, 'B'), (2, 'C'), (3, 'D')] down to [(1, 'B'), (2, 'C'), (3, 'D')], etc. while your version re-numbers that slice to [(0, 'B'), (1, 'C'), (2, 'D')]. This in turn leads to your erroneous output.

Start the inner loop at the higher index by adding a second argument to the enumerate() function, the index at which to start numbering:

def combinations2(items):
    result = []
    for n, item in enumerate(items):
        for m, item2 in enumerate(items[n:], n):
            if n != m:
                result.append((item, item2))
    return result

Back to a one-liner:

defcombinations2(items):
    return [(item, item2) for n, item inenumerate(items) for m, item2 inenumerate(items[n:], n) if n != m]

This then works correctly:

>>> defcombinations2(items):
... return [(item, item2) for n, item inenumerate(items) for m, item2 inenumerate(items[n:], n) if n != m]
... >>> letters = ['A','B','C','D']
>>> combinations2(letters)
[('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'C'), ('B', 'D'), ('C', 'D')]

Note that you can simplify it further; the only time when n == m is True is for the first iteration of each inner loop. Just slice the items list for the inner list one value further; start the outer enumerate() at 1, drop the inner enumerate() and drop the n != m test:

defcombinations3(items):
    result = []
    for n, item inenumerate(items, 1):
        for item2 in items[n:]:
            result.append((item, item2))
    return result

or as a list comprehension:

defcombinations3(items):
    return [(item, item2) for n, item inenumerate(items, 1) for item2 in items[n:]]

Solution 2:

Just skip the clashes in the iterator.

>>> letter = ['A', 'B', 'C', 'D']
>>> list ( (x,y) for n, x inenumerate(letter) for y in letter[n+1:])
[('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'C'), ('B', 'D'), ('C', 'D')]

Solution 3:

Suppose you just want to get the list of combinations.

defcombinations2(items):
    returnfilter(lambda (i,j): i <> j, [(i,j) for i in items for j in items])

letters = ['A','B','C','D']
print combinations2(letters)

The output I got is:

[('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'A'), ('B', 'C'), ('B', 'D'), ('C', 'A'), ('C', 'B'),    ('C', 'D'), ('D', 'A'), ('D', 'B'), ('D', 'C')]

Post a Comment for "List Comprehension Behavior In Python"