Python Forum

Full Version: inconsistent processing of list of tuples
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Context: game of checkers, populating list of possible moves, but if player must make a jump, non-jumping moves should be removed from the list.

Problem description: I have a block of code which iterates over a list of tuples, each three items long. It checks if the last item is a zero. If so, it should remove that tuple from the list. However, it seems to only remove some such items. The zeros are integers, not strings.

if jumps:  # jumps is a boolean signaling the need to remove non-jump moves
	print(possible)  # possible is list of allowed moves as tuples (starting square, end square, jumped square)
	for i in possible:
		print(i[2])
		if i[2] == 0:
			print('removing', i)
			possible.remove(i)
		else:
			print('not removed', i)
print(possible)
Output from print statements:
[('f8', 'g7', 0), ('h8', 'g7', 0), ('e7', 'f6', 0), ('b6', 'c5', 0), ('b6', 'a5', 0), ('d6', 'c5', 0), ('h6', 'g5', 0), ('e5', 'c3', 'd4')]
0
removing ('f8', 'g7', 0)
0
removing ('e7', 'f6', 0)
0
removing ('b6', 'a5', 0)
0
removing ('h6', 'g5', 0)
[('h8', 'g7', 0), ('b6', 'c5', 0), ('d6', 'c5', 0), ('e5', 'c3', 'd4')]

The process works for a few moves into the game, but then this happens. Above, it appears that it does not see the three remaining offenders, as if they are added later, but nothing new happens before the last print statement.

I am stumped.
It is bad design to modify a list inside a while loop that traverses the list. Here, you can do it with a one line list comprehension
possible[:] = [i for i in possible if i[2] != 0]
Gribouillis,
Thanks, that appears to have fixed the problem. But I do not understand why.
I changed the above block to this:
	if jumps:
		possible = [i for i in possible if i[2] != 0]
Could you explain why the former was "bad design"?

Also, I did not include the brackets and colon as in your example, is that necessary without start/end/step parameters?

Thanks
(Nov-05-2018, 01:38 AM)PyFool Wrote: [ -> ]Could you explain why the former was "bad design"?
Simply try to modify a list while iterating on it. It gives suprising results because the iterator uses an internal index to return L[0], L[1], ..., but as the contents of the list changes, some values are missing
>>> L = list(range(10))
>>> print(L)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> for i in L:
...     L.remove(i)
...     print(i, L)
... 
0 [1, 2, 3, 4, 5, 6, 7, 8, 9]
2 [1, 3, 4, 5, 6, 7, 8, 9]
4 [1, 3, 5, 6, 7, 8, 9]
6 [1, 3, 5, 7, 8, 9]
8 [1, 3, 5, 7, 9]
>>> 
(Nov-05-2018, 01:38 AM)PyFool Wrote: [ -> ]I did not include the brackets and colon as in your example, is that necessary without start/end/step parameters?
This is a special syntax called "list comprehension". It is documented here.

Another way to do it is to iterate over a copy of the initial list. Compare this code with the previous one
>>> L = list(range(10))
>>> print(L)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> for i in list(L):
...     L.remove(i)
...     print(i, L)
... 
0 [1, 2, 3, 4, 5, 6, 7, 8, 9]
1 [2, 3, 4, 5, 6, 7, 8, 9]
2 [3, 4, 5, 6, 7, 8, 9]
3 [4, 5, 6, 7, 8, 9]
4 [5, 6, 7, 8, 9]
5 [6, 7, 8, 9]
6 [7, 8, 9]
7 [8, 9]
8 [9]
9 []
I see. "del a" deletes the variable, while "del a[:]" only empties the list.

a = [1,2,3,4,5,6,7,8,9]
print(a)
del a[:]
print(a)

a = [1,2,3,4,5,6,7,8,9]
print(a)
del a
print(a)
Output:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
NameError: name 'a' is not defined

I learned new things about list comprehensions. Thanks a lot.