I am facing some difficulties in backtracking.
How do I define a global list to be used in the problems of backtracking? I saw a couple of answers and they have all suggested using 'global' keyword in front of the variable name, inside the function to use as global. However, it gives me an error here.
Is there any good general method that can be used to get the result, instead of a global variable?
The code below is trying to solve the backtrack problem wherein, a list of numbers is given, and we have to find unique pairs (no permutations allowed) of numbers that add to the target.
For example, given candidate set [2, 3, 6, 7] and target 7,
A solution set is:
[
[7],
[2, 2, 3]
]
///////////////////////////////CODE/////////////////////////////
seen = []
res = []
def func(candidates, k, anc_choice): #k == target
#global res -- gives me an error -- global name 'res' is not defined
if sum(anc_choice) == k:
temp = set(anc_choice)
flag = 0
for s in seen:
if s == temp:
flag = 1
if flag == 0:
seen.append(temp)
print(anc_choice) #this gives me the correct answer
res.append(anc_choice) #this doesn't give me the correct answer?
print(res)
else:
for c in candidates:
if c <= k:
anc_choice.append(c) #choose and append
if sum(anc_choice) <= k:
func(candidates, k, anc_choice) #explore
anc_choice.pop() #unchoose
func(candidates, k, [])
Can someone please provide me answers/suggestions?
To utilise the global keyword, you first need to declare it global before instantiating it..
global res
res = []
Though from looking at your code. Because the res = [] is outside the function, it is already available globally.
There are plenty of reasons why you should not use global variables.
If you want a function that updates a list in the above scope, simply pass the list as argument. Lists are mutable, so it will have been updated after the function call.
Here is a simplified example.
res = []
def func(candidate, res):
res.append(candidate)
func(1, res)
res # [1]
Related
I am new to python. Can someone please explain to me how this works in python?
How to change the value of item in the main function?
def calc(arr = []):
index = 0
for item in arr:
item = item + 1
arr[index] = item
index += 1
if __name__ == '__main__':
item = 1
calc([item])
print("item is => ", item)
If your aim is to change an immutable value in some way, this would be best:
def add_one(value):
return value + 1
def main():
# your code moved to a function, to avoid these variables inadvertently becoming globals
item = 1
# update item with the function result
item = add_one(item)
print("item is => ", item)
if __name__ == '__main__':
main()
From your example, it appears you want to update a list with each item in the list incremented by 1, you can do that the same way:
def add_one_all(values):
return [values + 1 for values in values]
def main():
items = [1, 4, 9]
# update items with the function result
items = add_one_all(items)
print("items are => ", items)
if __name__ == '__main__':
main()
However, since a list is mutable, you can update it from inside a function, by making the changes to the list in-place:
def add_one_all_inplace(values: list = None):
if values is not None:
for i in range(len(values)):
values[i] += 1
def main():
items = [1, 4, 9]
# update the content of items
add_one_all_inplace(items)
print("items are => ", items)
if __name__ == '__main__':
main()
The advantage of the latter solution is that no new list is created, which may be preferable if you need to be very frugal with space, or are only making a few changes to a very large list - your example would probably be better served with the second solution though.
Note that the way you called the function still wouldn't work in the latter case:
def main():
item = 1
add_one_all_inplace([item])
The list containing item would be changed to be [2], but that doesn't affect item itself. The list passed to add_one_all_inplace will just contain the value of item, not a reference to it.
you can do with the global keyword in python which allows us to use a non-global variable as global if we get an error like 'UnboundLocalError: local variable '' referenced before assignment' in that case we also use the global keyword I have done the same code with proper assignment in the below image
as you have declared variable in the wrong place because that would only be accessible inside the if block only so you have to place the variable at the upper level and what will happen if I don't use the global keyword see the below image
you can see the difference
and if you like my answer please follow me and upvote my answer
temp = [i for i in range(1,len(arr)+1)]
for i in range(len(temp)):
temp = shift_right(temp,len(temp)-i-1,arr[i])
print(temp)
solve(arr,temp)
print(temp)
this is in main. arr is another function telling how much to shift right/left
def solve(arrs,temps):
for i in range(len(temps)):
temps = shift_left(temps,i,arrs[i])
return temps == [i for i in range(1,len(temps)+1)]
def shift_left(arrs, i , no_of_times):
mid = arrs.pop(i)
return arrs[:i-no_of_times:]+[mid]+arrs[i-no_of_times::]`
def shift_right(arrs, i , no_of_times):
mid = arrs.pop(i)
return arrs[:i+no_of_times:]+[mid]+arrs[i+no_of_times::]
these are my function definitions. Now here is the problem. i have not used global variable names or even used "global" keyword.
then why does my output look like this?
[2, 3, 1]
[3, 1]
why did temp remove first element for no reason?
When passing the list, it is being send by reference not by value, so when it does the pop on shift_left it affects the original one, my solution around this is making a copy, something like this:
def shift_left(arrs, i , no_of_times):
newArrs=arrs[:]
mid = newArrs.pop(i)
# print(temp)
return arrs[:i-no_of_times:]+[mid]+arrs[i-no_of_times::]
in short, python is passing the list by reference not by value by default
I'm having trouble with understanding when to use the return function. In the below function my intuition is that the return statement should be there to return the modified list, but my TA said is was redundant which I didn't quite understand why. Any clarification on when to correctly use return statement and on common practise would be highly appreciated.
p = [2,0,1]
q = [-2,1,0,0,1,0,0,0]
p1 = [0,0,0,0]
#Without return statement
def drop_zeros1(p_list):
"""drops zeros at end of list"""
i = 0
while i < len(p_list):
if p_list[-1]==0:
p_list.pop(-1)
else:
break
#With return statement
def drop_zeros(p_list):
"""drops zeros at end of list"""
i = 0
while i < len(p_list):
if p_list[-1]==0:
p_list.pop(-1)
else:
return p_list
break
Also why the output is inconsistent when used on the list p1, it only removes the last 0 when it should remove all zeroes?
Many Thanks,
The convention is that functions either mutate the argument(s) given to it, or return the result, but then leave the arguments untouched.
This is to prevent that a user of your function would do this:
template = [1, 2, 0, 0]
shorter = drop_zeros(template)
print ("input was ", template, " and output was ", shorter)
They would expect this output:
input was [1, 2, 0, 0] and output was [1, 2]
... but be surprised to see:
input was [1, 2] and output was [1, 2]
So to avoid this, you would either:
not return the modified argument, but None. That way the above code would output ...and output was None, and the user would understand that the function is not designed to return the result.
return the result, but ensure that the argument retains its original content
So in your case you could do:
def drop_zeros(p_list):
"""drops zeroes at end of list, in-place"""
while p_list and p_list[-1] == 0:
p_list.pop()
Note that the else can be better integrated into the while condition. No more need to do an explicit break. Also .pop() does not need -1 as argument: it is the default.
If you prefer a function that returns the result, then the logic should be somewhat different:
def drop_zeros(p_list):
"""returns a copy of the list without the ending zeroes"""
for i in range(len(p_list)-1, -1, -1):
if p_list[i] != 0:
return p_list[0:i+1]
return []
Now the code is designed to do:
template = [1, 2, 0, 0]
shorter = drop_zeros(template)
print ("input was ", template, " and output was ", shorter)
# input was [1, 2, 0, 0] and output was [1, 2]
Your TA is right, the return is redundant because of what in python is called aliasing.
Basically, in your function, p_list is a reference (NOT a copy) to whatever list you pass in when you call the function. Since you use pop, which mutates the list in-place when extracting an element, p_list will be modified and this modification will be visible outside the function:
drop_zeros(q) # from here, in the function, p_list is q (as in, exactly the same object)
print(q)
prints
[-2,1,0,0,1]
I'm not sure why l would be modified by the find() function. I thought since I'm using a different variable in another function, l would not be modified by the function since it's not global.
I made sure it wasn't an error in the code by copy and pasting l = [2, 4, 6, 8, 10] before every print statement, and it returned the right outputs, meaning l is being changed by the function. I also removed the main function from the main and basically made it outright global, but it still gave the original bad results.
I'm not sure if this is an issue with my understanding of Python since I'm a beginner in it and I'm coming from Java.
Here's the code and results:
def find(list, user):
while True:
n = len(list)
half = int(n/2)
if n == 1:
if user != list[0]:
return "Bad"
else:
return "Good"
elif user == list[half]:
return "Good"
elif user > list[half]:
del list[0:half]
elif user < list[half]:
del list[half:n]
print(list)
if __name__ == "__main__":
l = [2, 4, 6, 8, 10]
print(find(l, 5)) # should print Bad
print(find(l, 10)) # should print Good
print(find(l, -1)) # should print Bad
print(find(l, 2)) # should print Good
but it returns with this
[2, 4]
[4]
Bad
Bad
Bad
Bad
You should read this question at first. why can a function modified some arguments while not others.
Let me rewrite your code for clarification.
def find(li, el):
# li is a list, el is an integer
# do something using li and el
if __name__ == "__main__":
l = [1,2,3,4]
e = 2
find(l, e)
The function find received two objects as parameters, one is li and the other is el. In main, we defined two objects, a list, we called it l, and an integer, we called it e. Then these two objects was passed to find. It should be clear that it is these two objects that passed to the function, not the name. Then your find function has access to this object, called l in main, while called li in find. So when you change li in find, l changed as well.
Hope that answers your question. And to fix this, check deepcopy.
Arguments in Python are passed by assignment. https://docs.python.org/3/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference
In your case, that means that the list parameter of your find function is assigned the exact same list you pass in as the argument l. So, when you modify list (which is a very bad name since it shadows Python's list keyword), you also modify l, since no copy of the original was made.
You could use copy() to pass in a copy, but I think you would do well to reconsider the function as a whole, since it currently has many, many issues and you're likely to end up with a solution that won't suffer from having the original list passed in.
I have never come across this before and wonder if anyone has a solution. I have a while loop with a function inside that returns an integer. The function takes a string and a list. The first iteration of the while loop returns the correct answer but on subsequent iterations the list seems to be empty even though the function does not change the list in any way. Also if i try to reset the list after the function in the while loop the new list also seems to be empty. It seems to be very strange behavior. Any explanation as to whats going on would be much appreciated. The code of the function inside the loop is quite long so at this stage I will avoid posting it. However if requested then I will post.
spectrum = [1,2,3,4,5,6]
leaderboard = ['zzz','xxx','yyy']
pep_scores = []
j=0
original_spectrum = spectrum
print len(original_spectrum)
while j < len(leaderboard):
x= linear_score(leaderboard[j],spectrum) #this function doesn't alter spectrum
print leaderboard[j], x
spectrum = original_spectrum #should reset spectrum even though it shouldn't be necessary to do that
print len(spectrum), len(original_spectrum) #prints 2 empty lists
pep_scores.append(x) #appends correct score on 1st iteration and '0' for all others
j=j+1
I had added print statements to try to resolve the problem, my original code did not contain 'original_spectrum = spectrum' or 'spectrum = original_spectrum' in the while loop. I don't understand why after 1 iteration 'origninal_spectrum' is an empty list. I haven't posted the function because I cannot see how it could be causing the problem. Please ask if you need more information.
To create copy of the list use copy_list = original_list[:].
So in your example:
spectrum = [1,2,3,4,5,6]
leaderboard = ['zzz','xxx','yyy']
pep_scores = []
j=0
original_spectrum = spectrum[:]
print len(original_spectrum)
while j < len(leaderboard):
x= linear_score(leaderboard[j],spectrum) #this function doesn't alter spectrum
print leaderboard[j], x
spectrum = original_spectrum[:] #should reset spectrum even though it shouldn't be necessary to do that
print len(spectrum), len(original_spectrum) #prints 2 empty lists
pep_scores.append(x) #appends correct score on 1st iteration and '0' for all others
j=j+1
Its because of that you define spectrum outside the function and its scope is global , and when you pass spectrum to your function as its the name of list any changes on it change it globally not local in your function ! and note that its just about mutable (like lists) objects . (Note : labels are pointer to special memory addresses ) (your copy command original_spectrum = spectrum just make 2 label for one object !!! )
for better understanding see the below example :
>>> a=[1,2,3]
>>> def f(s):
... s.remove(1)
...
>>> f(a)
>>> a
[2, 3]
>>> def f(s):
... s+=[1,2]
...
>>> f(a)
>>> a
[2, 3, 1, 2]
Now you have 2 choice :
make a copy of spectrum and pass that to function :
copy_spectrum = spectrum[:]
define spectrum inside the function and one outside for global usage !