I encountered this issue while working on a basic graph traversal leetcode problem.
I've included my entire answer below, a basic BFS. But my question really concerns to what is happening with my set() variable named "seen" in the while loop.
Basically, I initialize it to an empty set before the while loop, expecting it to cumulatively grow with each value popped from the queue being added to this set, from within the while loop.
Instead, it seems that on each iteration of the while loop, the contents of "seen" somehow revert back to being empty, despite not resetting it in the code.
I have added some comments to clarify where I'm referring to:
import queue
def canFinish(numCourses, prerequisites):
prereqs = {}
buildPrereqs(prereqs, prerequisites)
for course in range(numCourses):
seen = set() # initialize set outside of while loop
q = queue.Queue()
q.put(course)
while (not q.empty()): # while loop
popped = q.get()
print("popped:", popped)
print("seen at beginning:", seen)
if popped in seen:
return False
seen.add(popped) # populate set, but resets before next iteration (?)
if popped in prereqs:
for prereq in prereqs[popped]:
q.put(prereq)
print("seen at end:", seen)
return True
def buildPrereqs(prereqs, prerequisites):
prereqs = {}
for pair in prerequisites:
course = pair[0]
prereq = pair[1]
if not course in prereqs:
prereqs[course] = set()
prereqs[course].add(prereq)
print(canFinish(2, [[1,0],[0,1]]))
If I run canFinish(2,[[1,0],[0,1]]), which is one of the example cases at the leetcode link, I get the following output from my print statements:
popped: 0
seen at beginning: set()
seen at end: {0}
popped: 1
seen at beginning: set()
seen at end: {1}
Whereas I am expecting something like this:
popped: 0
seen at beginning: set()
seen at end: {0}
popped: 1
seen at beginning: {0}
seen at end: {0, 1}
I tried using a dictionary in place of the set() and encountered the same thing. Can someone help me identify the source of this Python behavior?
Related
I was trying to find out a way to perform in-order tree transversal iteratively without using a tuple. Not sure why but the list is going from [] to [None] and runs the while loop one more time
https://leetcode.com/problems/binary-tree-inorder-traversal/description/
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
stack = [root]
res = []
while stack:
print("start",stack)
curr = stack.pop()
if curr.left:
temp = curr.left
curr.left = None
if curr.right:
stack.append(curr.right)
curr.right = None
stack.append(curr)
stack.append(temp)
else:
res.append(curr.val)
if curr.right:
stack.append(curr.right)
print("end",stack)
return res
when running this code with testcase [1], the output when printing is
start [TreeNode{val: 1, left: None, right: None}]
end []
start [None]
this means at the end of the while loop stack = [], so the while loop should end. However, all of a sudden the stack = [None] and runs one more loop. Very confused why this happens. I put a print in front of every append but found nothing. It is solved by adding a check for if curr is None but I still have no idea why this happens. If anyone knows please help!
UPDATE:
Copy and pasted into hackerrank's same question and it worked properly. Could this be a bug in leetcode?????
https://www.hackerrank.com/challenges/tree-inorder-traversal/problem
I am assuming what you are trying to do here is convert a tree to an in-order list of elements to look at. It is possible you're trying to do something else, but if so, it is unclear.
def inOrderTraversal(ls: TreeNode) -> List[int]:
// Recurse into the left branch
if isinstance(ls.left, int):
left = [ls.left]
else: // Assumes left is a TreeNode
left = inOrderTraversal(ls.left)
// Recurse into the right branch
if isintance(ls.right, int):
right = [ls.right]
else:
right = inOrderTraversal(ls.right)
return left + right
This almost certainly won't work right out of the box, but it is a demonstration of what you're going for. You need to determine if the left branch should be recursed down, then the right. If the left or right branches are simply ints, then you need to add them as elements.
Your while loop is a misdirection - you are already setting stack to be a list of length one (the root element). The while loop isn't doing anything since you have only one element. You do not need to set anything to None for this sort of task - just forget about whatever you were iterating over once you have the data you need.
I am learning Python and have been finding that for some reason my for loop will stop, even when there is more 'loops' to go.
This happens ONLY when I delete an item from a list. If the removal is commented out, it works: printing "RUNNING LISTING" twice and it will check 2 items against 2 in the list. If an item is deleted it will print once and will check only 1 item agaisnt the other 2. Both times the console has no error.
It stops with an independant list variable when the original isnt affected: independant_list = looping_list. and also stops with list.remove(x), list.pop(x) and del list[x].
currently the problem code is: del listings_to_add[listing_count]
Here is my code, in short (I think):
for A in ListX:
for B in ListY:
if A in ListY:
ListX - remove(A)
And in Long:
listings_to_add = new_listing_list
for listing_count, listing in enumerate(new_listing_list):
found = False
print('\nRUNNING LISTING\n')
#Checks the listing against all in DB
for count, i in enumerate(id_in_DB):
if not found:
# Checks links - cuts them to listingID
if str(listing[4]).split('?rsqid')[0] == i[1].split('?rsqid')[0]:
same_link = True
del listings_to_add[listing_count]
found = True
else:
print(f'Different link: "{listing[4]}" \nWith DB.......: "{i[1]}"')
I have read other posts about why deleting from a list you are running through makes bugs, which is what I had done first, but it still stops after changes. Any help would be appreciated.
Your problem is that you are iterating new_listing_list, but listings_to_add is an alias, so when you del listings_to_add[listing_count], you are affecting new_listing_list.
I decluttered your code. Try this:
listings_to_add = []
for listing_count, listing in enumerate(new_listing_list):
for count, i in enumerate(id_in_DB):
if str(listing[4]).split('?rsqid')[0] == i[1].split('?rsqid')[0]:
same_link = True
break
listings_to_add.append(listing)
print(f'Different link: "{listing[4]}" \nWith DB.......: "{i[1]}"')
you can use list comprehensions to tackle this
for A in ListX:
for B in ListY:
if A in ListY:
ListX - remove(A)
as
listX = [x for x in listX if x not in listY]
In the following Function I tried to simulate an wide search alike function.
First Function 'Hello' by giving and search name = V, is supposed to give you all the tuple partners that it has in a list.
Second Function is supposed to have two lists, first for already visited and the ones who are still in a list i.e. que.
With two for loops I went through newly generated list by the word given in, so that I can use those words to generate further tuple partners, that aren't in besucht list. After the usage has been done the item will be deleted from the 'Liste' and appended to besucht.
Question: It doesn't work as I intended and I don't understand why
V = {'CGN', 'FRA','KEL','MUC','PAD','SFX','STR','TXL'}
E = {('SFX','TXL'),('FRA','CGN'),('FRA','MUC'),('FRA','STR'),('CGN','FRA'),('STR','FRA'),('TXL','SFX'),('CGN','PAD'),('PAD','KEL'),('MUC','KEL'),('KEL','STR') }
S = {('A','B'),('A','B')}
def Hallo(V,E):
Total = []
Que = []
for i in E:
for j in i:
if j == V:
Total.append(i)
for i in Total:
for a in i:
if a != V:
if a not in Que:
Que.append(a)
return Que
def Durchsuchen(V,E):
besucht = []
Liste = []
Liste.append(Hallo(V,E))
besucht.append(V)
while len(Liste) !=0
for i in Liste:
if i not in besucht:
besucht.append(i)
Liste.remove(i)
Liste.append(Hallo(i,E))
print Liste
print besucht
print Durchsuchen('FRA',E)
What is supposed to do? It's supposed to give you all the possibilities i.e if you give in 'FRA' it will generate [MUC, STR, CGN] since MUC and etc is within this list this should also give you KEL for example. I.E all the possible options that are out there.
Well your problem is that you use remove() on a list inside a loop that iterates through it - which should never be done. This changes the logic that the loop relies on and causes your unexpected results.
The first function works just fine so I'd suggest you to use it once on your first word 'FRA', and then use it on every word that pairs with it - [MUC, STR, CGN] in your example. Then use the following code on the lists that you got:
newList = []
for ls in listOfLists:
for word in ls:
if word not in newList:
newList.append(word)
My code is as follows:
self.newMonsterList =[]
myKeys = []
if (len(self.myList)>0):
for i in range(len(self.myList)-1):
myVar = self.myList[i]
myKeys.append(myVar[1])
lis = game.active_entities.keys()
for k in lis:
if (k not in myKeys):
game.active_entities[k].destroy()
for it in self.myList:
if (it[1] in lis):
self.setEnt(it, game.active_entities[it[1]])
else:
self.newMonsterList.append(it)
self.makeEnt()
This code, that is the method in which it is, is called in a while loop which apparently works.
makeEnt appends an Entity with it[1] as Key to self.newMonsterList.
This works, I have tested it. Therefore, an ID which is in newMonsterList after the first full round of all this code (i.e. of the while loop which calls this code) should not be there after the second.
It is, however.
If, on the other hand, I check in makeEnt whether or not the content of newMonsterList already is a key, I get correct results.
I therefore conclude that newMonsterList is not properly emptied in which run of the while-loop.
I have been trying different versions for 2 hors now....anyone got an idea?
I am trying to write that down simpler:
a is a list of Ints
d is a dict with ints as keys
While True:
b=[]
for it in a:
if (it not in d):
b.append(it)
print it
else:
print it
for k in b:
d[k] = k
I found out now that the monsterlist is indeed emptied at each turn, however, in the ifclause ("if (it not in d):"), "it" is appened to the list each turn. However, "it" is also definitely in d after the first iteration, I can print it.
You are aware that this loop iterates over self.myList, but skips the last item in the list, right?
myKeys = []
if (len(self.myList)>0):
for i in range(len(self.myList)-1):
myVar = self.myList[i]
myKeys.append(myVar[1])
lis = game.active_entities.keys()
That seems suspicious to skip the last item.
FYI, I think the above code is equivalent to:
assert self.myList # Because if self.myList was empty, iterating over
# lis (later) would have caused an NameError.
myKeys = [value for key, value in self.myList[:-1]]
lis = game.active_entries.keys()
Below is a simple function to remove duplicates in a list while preserving order. I've tried it and it actually works, so the problem here is my understanding. It seems to me that the second time you run uniq.remove(item) for a given item, it will return an error (KeyError or ValueError I think?) because that item has already been removed from the unique set. Is this not the case?
def unique(seq):
uniq = set(seq)
return [item for item in seq if item in uniq and not uniq.remove(item)]
There's a check if item in uniq which gets executed before the item is removed. The and operator is nice in that it "short circuits". This means that if the condition on the left evaluates to False-like, then the condition on the right doesn't get evaluated -- We already know the expression can't be True-like.
set.remove is an in-place operation. This means that it does not return anything (well, it returns None); and bool(None) is False.
So your list comprehension is effectively this:
answer = []
for item in seq:
if item in uniq and not uniq.remove(item):
answer.append(item)
and since python does short circuiting of conditionals (as others have pointed out), this is effectively:
answer = []
for item in seq:
if item in uniq:
if not uniq.remove(item):
answer.append(item)
Of course, since unique.remove(item) returns None (the bool of which is False), either both conditions are evaluated or neither.
The reason that the second condition exists is to remove item from uniq. This way, if/when you encounter item again (as a duplicate in seq), it will not be found in uniq because it was deleted from uniq the last time it was found there.
Now, keep in mind, that this is fairly dangerous as conditions that modify variables are considered bad style (imagine debugging such a conditional when you aren't fully familiar with what it does). Conditionals should really not modify the variables they check. As such, they should only read the variables, not write to them as well.
Hope this helps
mgilson and others has answered this question nicely, as usual. I thought I might point out what is probably the canonical way of doing this in python, namely using the unique_everseen recipe from the recipe section of the itertools docs, quoted below:
from itertools import ifilterfalse
def unique_everseen(iterable, key=None):
"List unique elements, preserving order. Remember all elements ever seen."
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
# unique_everseen('ABBCcAD', str.lower) --> A B C D
seen = set()
seen_add = seen.add
if key is None:
for element in ifilterfalse(seen.__contains__, iterable):
seen_add(element)
yield element
else:
for element in iterable:
k = key(element)
if k not in seen:
seen_add(k)
yield element
def unique_with_order(seq):
final = []
for item in seq:
if item not in final:
final.append(item)
return final
print unique_with_order([1,2,3,3,4,3,6])
Break it down, make it simple :) Not everything has to be a list comprehension these days.
#mgilson's answer is the right one, but here, for your information, is a possible lazy (generator) version of the same function. This means it'll work for iterables that don't fit in memory - including infinite iterators - as long as the set of its elements will.
def unique(iterable):
uniq = set()
for item in iterable:
if item not in uniq:
uniq.add(item)
yield item
The first time you run this function, you will get [1,2,3,4] from your list comprehension and the set uniq will be emptied. The second time you run this function, you will get [] because your set uniq will be empty. The reason you don't get any errors on the second run is that Python's and short circuits - it sees the first clause (item in uniq) is false and doesn't bother to run the second clause.