List Comprehension Behavior In Python
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"