I'm having a problem with my code with python variables - python

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

Related

Python: List in global namespace gets unintentionally modified through function

I have a propably very basic problem in Python. However, I would be very thankful, if someone could help me to understand, what is happening here:
My code is as follows:
purchaseprices = {'Stock_A': [[10, 4.21],[20, 5.23], [5, 8.32]],
'Stock_B': [[5, 8.23],[15, 7.42], [10, 7.53]]}
def update_purchaseprices(name, number_of_shares):
remaining_number_of_shares = number_of_shares
updated_purchaseprices = purchaseprices[name][:]
indices_to_delete = []
for i in range(len(updated_purchaseprices)):
if updated_purchaseprices[i][0] < remaining_number_of_shares:
remaining_number_of_shares -= updated_purchaseprices[i][0]
indices_to_delete.append(i)
else:
updated_purchaseprices[i][0] = updated_purchaseprices[i][0] - remaining_number_of_shares
break
updated_purchaseprices = [i for j, i in enumerate(updated_purchaseprices) if j not in indices_to_delete]
return updated_purchaseprices
name = "Stock_A"
number_of_shares = 34
print(purchaseprices['Stock_A'])
updated_purchaseprices = update_purchaseprices(name, number_of_shares)
print(updated_purchaseprices) # this works as expected
print(purchaseprices['Stock_A']) # why did the original list got changed as well?
Here is, what I wanted to do: I have an original list, which is stored in a dictionary called purchaseprices. This list can be accessed by purchaseprices['Stock_A’]. Now I tried to write a function to return a list called updated_purchaseprices, which is basically a modified version of the original list. In order to leave the original list unchanged, I made a copy of it by including updated_purchaseprices = purchaseprices[name]:. Unfortunately my code nevertheless also changes the original list. Can someone please tell me why this is happening?
As you probably know because you used [:], a list is mutable and you need to take a copy in your function. But the copy still contains the original objects (sublists).
You need to copy those too!
Replace:
updated_purchaseprices = purchaseprices[name][:]
with:
updated_purchaseprices = [l.copy() for l in purchaseprices[name]]
or:
import copy
updated_purchaseprices = copy.deepcopy(purchaseprices[name])

Fibonacci series by recursive function in Python

Hello I am trying to generate Fibonacci series by using a recursive function in python.
Here is my code
def fibolist(n):
list1 = [1, 1]
if n in (1,2) :
return list1
else:
fibolist(n-1).append(sum(fibolist(n-1)[n-3:]))
return list1
but when I enter any number as an argument, the result is [1, 1]
Could you please help me?!
You start with
list1 = [1, 1]
You never change that value, and then you return it to the calling routine.
Each invocation of fibolist has a local variable named list1; appending to one does not change the list1 value in the calling program. You need to explicitly do that. Try
else:
return fibolist(n-1) + [sum(fibolist(n-1)[n-3:])]
Just to fix your code:
def fibolist(n):
if n in (0,1) :
return [1,1]
else:
return fibolist(n-1)+[sum(fibolist(n-1)[n-2:])]
Few notes:
lists in python have starting index=0, so it's better start with it (unless you want to put start return to [0,1,1] for n in (1,2)).
Also - as already mentioned you shouldn't return local variable, which you preassign in each go.
Your code is not updating the list1 variable that it returns upon coming back from the recursion. Doing fibolist(n-1).append(...) updates the list returned by the next level but that is a separate list so list1 is not affected.
You could also make your function much simpler by making it pass the last two values to itself:
def fibo(n,a=1,b=1): return [a] if n==1 else [a] + fibo(n-1,b,a+b)
BTW, the modern interpretation of the fibonacci sequence starts at 0,1 not 1,1 so the above signature should be def fibo(n,a=0,b=1).
Output:
print(fibo(5))
#[1, 1, 2, 3, 5]

Local variable referenced before assignment - error

This function should add 1 to a number given in the form of a list.
If the list is [1, 2, 3], then this function should return [1, 2, 4] because 123 + 1 == 124.
Below is the function in question:
def plusOne(self, A):
val = 1;
for i in range(len(A)-1,0,-1):
val = val + A[i]
borrow = int(val/10)
if borrow == 0:
A[i] = val
break;
else:
A[i] = val%10
val = borrow
A = [borrow] + A
while A[0]==0:
del A[0]
return A
The error message is:
Traceback (most recent call last):
File "main.py", line 225, in
Z = obj.plusOne(A)
File "/tmp/judge/solution.py", line 8, in plusOne
A = [borrow] + A
UnboundLocalError: local variable 'borrow' referenced before assignment
And surprisingly the below code runs without any error:
class Solution:
# #param A : list of integers
# #return a list of integers
def plusOne(self, A):
val = 1;
for i in range(len(A),0,-1):
val = val + A[i-1]
borrow = int(val/10)
if borrow == 0:
A[i-1] = val
break;
else:
A[i-1] = val%10
val = borrow
A = [borrow] + A
while A[0]==0:
del A[0]
return A
I still do not understand that if initializing the borrow variable directly inside the loop is causing the error then the above snippet should throw the error too, right?
borrow is getting declared/initialized inside the for loop.
for i in range(len(A)-1,0,-1):
What if len(A) is 1 then there wont be any range to loop through and borrow will never come into scope, hence the error.
The problem you are facing is a classic problem of scoping.
The variable borrow is not defined before the for loop and the code tries to access it after the loop.
If the len(A) > 1, your code will work perfectly because in that case borrow is defined in the loop and loop is being executed.
But, if the len(A) <= 1, in such case the code is directly trying to access the variable borrow, which has never been defined before.
In such scenarios, the best practice is to define the variables with a default value.
In this case borrow = 0, would be the correct value, if you define it before the for loop.
I hope this helps :)
There are few problems with your code. It does work as intended, in most cases. Allow me to suggest few improvements and point out the problems.
First of all, you have two semicolons in your code, they are not needed in Python.
(line 2) val = 1; and (line 8) break;
Second, when lists are sent to methods and functions they are sent as reference, and as such, changes inside those methods would apply to the original list.
Third, a break within a loop usually shows a wrong choice and planning.
Fourth, if plusOne is not part of a class then there is no need for self as it's parameters (if it is part of a class then leave it there and call using self.plusOne(..) when calling.
Here is how I would have done that, for different point of view:
def plusOne(self, lst):
if len(lst) > 0:
# Initial value to increase by
val = 1
# Initialization of list index to last element.
i = -1
# Calculate the number after addition, calculate the borrow and replace in the list.
num = val + lst[i]
borrow = num // 10
lst[i] = num % 10
# While there is any borrow and the list didn't read the first element.
while borrow != 0 and i > len(lst)*-1:
# Update the borrow to new value.
val = borrow
# Decrease the index.
i -= 1
# Calculate the number after addition, calculate the borrow and replace in the list.
num = val + lst[i]
borrow = num // 10
lst[i] = num % 10
# Check if borrow remaining after index out of range.
if borrow != 0:
# Insert borrow before the first element.
lst.insert(0, borrow)
# Remove leading zeros.
while lst[0] == 0:
del lst[0]
def main():
lst = [1, 2, 9]
plusOne('', lst)
print(lst)
if __name__ == '__main__':
main()

What is common pratice with return statements in functions?

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]

Global variable for recursion in Python

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]

Categories

Resources