Local variable referenced before assignment - error - python

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()

Related

Binary Search in Python - Iterative Method

so I'm trying to learn programming myself in Python language and was confused implementing Binary Search in Python. So here's what I have done
list = [3,6,8,12,14,17,25,29,31,36,42,47,63,55,62]
key = 42
print(list)
def high(sorted_list):
max_index = len(sorted_list)
return max_index
def low(sorted_list):
start_index = 0
return start_index
def mid(sorted_list):
mid_index = ( low(sorted_list) + (high(sorted_list) - low(sorted_list)) ) // 2
return mid_index
for x in range(4):
if list[mid(list)] < key:
list = list[mid(list)+1:]
elif list[mid(list)] < key:
list = list[mid(list)-1:]
print(list)
I know I should not keep a range number in for loop but I know it will only make 4 comparisons in this example so I hardcoded it. But when I run it, it splits the list only once and keep on printing the second half of the list. Output image:
Ok, I tried your code and had to do a few corrections:
The while loop had to be modified (you knew that)
There wasn't a check for the case where the key is found (see comments)
There was a typo, < instead of > (see comments)
In the same line, the list partition was wrong
The low function was useless (returning a constant value) (see comments)
The high function was useless too (simply returning the value from another function)
The mid function was more complicated than needed (it boiled down to taking a value, then adding and subtracting zero), so it can simply take the value
Ah, btw the input list is not sorted in your example.
This is my proposal:
def mid(lst):
return len(lst) // 2
def bin_search(lst, k):
while lst:
i = mid(lst)
if lst[i] == k:
return True
if lst[i] < k:
lst = lst[i+1:]
elif lst[i] > k:
lst = lst[:i]
else:
return False
bin_search([3,6,8,12,14,17,25,29,31,36,42,47,55,62,63], 42)
True
bin_search([3,6,8,12,14,17,25,29,31,36,42,47,55,62,63], 58)
False

Recursive behavior

Why does the following executes such that the print statement is called as often as it recursed but the count variable, count, when x == 1 is never reached.
def count_bits(n, count = 0):
x = n % 2
if n == 1:
return count + 1
if n < 1:
return count
if x == 1:
count += 1 # when x == 1
count_bits(int(n/2), count)
print("counter")
return count
why is it necessary to recurse with the return statement? Because if the recursive call is above the return statement the code
returns the wrong output but with the recursive call called with
return keyword, everything works well. Typically, the print statement
prints 'counter' as often as it recursed showing that the recursive call
works.
On the other hand, if "return" follows after the recursive call, it returns the count from the base condition, correctly.
def count_bits(n, count = 0):
x = n % 2
if n == 1:
return count + 1
if n < 1:
return count
if x == 1:
count += 1
return count_bits(int(n/2), count)
You have to return recursion result, as reccurent function meaning is to count current step you have to get result of previous step
F_k = F_k-1 * a + b # simple example
Means that you have to get result of F_k-1 from F_k and count current result using it.
I advised you to use snoop package to debug your code more efficient , by this package you can track the executing step by step.
to install it run:
Pip install snoop
Import snoop
add snoop decorator to count_bits()
for about the package see this link
https://pypi.org/project/snoop/
the difference in the output between the two methods is because of the way in which python handles fundamental data types. Fundamental data types such as float, ints, strings etc are passed by value, whereas complex data types such as dict, list, tuple etc are passed by reference. changes made to fundamental data types within a function will therefore only be changed within the local scope of the function, however changes made to a complex data type will change the original object. See below for an example:
x = 5
def test_sum(i : int):
print(i)
i += 5
print(i)
# the integer value in the global scope is not changed, changes in test_sum() are only applied within the function scope
test_sum(x)
print(x)
y = [1,2,3,4]
def test_append(l : list):
print(l)
l.append(10)
print(l)
# the list in the global scope has '10' appended after being passed to test_append by reference
test_append(y)
print(y)
This happens because it's far cheaper computationally to pass a reference to a large object in memory than to copy it into the local scope of the function. For more information on the difference, there are thousands of resources available by searching "what is the difference between by reference and by value in programming".
As for your code, it seems to me the only difference is you should alter your first snippet as follows:
def count_bits(n, count = 0):
x = n % 2
if n == 1:
return count + 1
if n < 1:
return count
if x == 1:
count += 1 # when x == 1
# assign the returned count to the count variable in this function scope
count = count_bits(int(n/2), count)
print("counter")
return count
The second code snippet you wrote is almost identical, it just doesn't assign the new value to the 'count' variable. Does this answer your question?

Why does my code only work when running the first function but the second function gives me a referenced before assignment variable?

I've tried changing the name of the variable thinkig that was the issue but that didn't fix it. I'm trying to calculate the additive persistence and multiplicative persistence and the additive and multiplicative roots of a number entered into the get_numbers function. For the additive_calculator and multiplicative_calculator functions whichever one I call first works, but the second one gives me an error at the print statement saying that the value of of the root, which I called total and total2 in this case, gives me a referenced before assignment error. I have no idea what to do to fix this error.`enter code here
from functools import reduce
def get_numbers():
num = (int(input("Please enter an integer(negative integer to quit):")))
nums = [int(a) for a in str(num)]
return(nums)
nums = get_numbers()
print(nums)
def additive_calculator(nums):
print("Additive loop")
counter = 0
while len(nums) > 1:
for num in nums:
len(nums)
total = 0
total = sum(nums)
list.clear(nums)
nums = [int(a) for a in str(total)]
print("sum:", total)
print(len(nums))
counter = counter + 1
print("Additive persistence", counter,",", "Additive Root:", total)
print("DONE")
def multiplicative_calculator(nums):
print("multiplicative loop")
counter = 0
while len(nums) > 1:
for num in nums:
len(nums)
total2 = 0
total2 = reduce(lambda x, y: x*y,(nums))
list.clear(nums)
nums = [int(a) for a in str(total2)]
print("sum:", total2)
print(len(nums))
counter = counter + 1
print("multiplicative persistence", counter,",", "multiplicative Root:", total2)
print("DONE")
multiplicative_calculator(nums)
additive_calculator(nums)
If you assign to a variable anywhere in a function, that variable is considered a local variable (unless declared global or nonlocal). It doesn't matter if you actually do the assignment when the function runs; it suffices that there is some statement in the function that assigns to it.
For example, in the function below, you assign to the variable a only if the argument b is true, but a is still a local variable if you don't assign to it (when b is false).
def f(b):
if b:
a = "foo"
return a
f(True) # -> "foo"
f(False) # error
It's the same with loops. If you assign to a variable in the body of a loop, or the variable in a for loop, that variable is a local variable even if you don't execute the body of the loop. If you execute the body, the variable is assigned to, and it is safe to read it, but if you do not execute the body, the variable isn't initialised, and it is an error to read it.
def f(x):
for a in x:
pass
return a
f([1, 2, 3]) # -> 3
f([]) # error
In your code, if nums is empty, total2 is never assigned to. It is still a local variable because there is an assignment to it in the code--it doesn't matter that the statement is never executed--but it is an error to read the variable if you haven't assigned to it.
The fix is simple: make sure you initialise total2 regardless of whether you enter the loop body or not.
def multiplicative_calculator(nums):
print("multiplicative loop")
counter = 0
total2 = 0 # move initialisation of total2 here
while len(nums) > 1:
# same code as before...
# when you get here, total2 is initialised whether you
# entered the loop or not.
print("multiplicative persistence", counter,
",", "multiplicative Root:", total2)

I defined a function, but i didn't get any value while I call that function

def number(n):
x=[]
for i in range (1,n):
if i%2==0:
x=x+[i]
i=i+1
return(n)
In the above code, no error is showing while running, but no value returned
As already mentioned in the comments section, there are a few things to note:
A function needs to be called (in case it was not done), so in your case you have to call the function by number(10) for example.
Probably you wanted to return x instead of n.
i = i+1 is not needed since range() already returns an iterable, therefore it has to be removed
the code in front of return does not need to be inside parantheses
you must change your code :
def number(n):
x = []
for i in range(1, n):
if i % 2 == 0:
x = x + [i]
i = i + 1
return (x) #---> you must return x not n!
print(number(12))
output:
[2, 4, 6, 8, 10]
difference between return and print in python :
this

How to rewrite this function as a recursive function?

def digits(n):
res = []
while n > 0:
res.append(n % 10)
n /= 10
return res
I want to rewrite this function so it uses recursion. I'm currently lost as to what to do. Can anyone give me some direction?
To create a recursive function you need to determine two things:
1) The base case - the condition on which you want to stop recursion
2) The general case - what to do on every input but the base case
You have already found both of these things, the base case is the while loop condition and the general case is the inside of the while loop. Try to use this information to move forward.
Here's a possible solution:
def digits(n):
if n < 10:
return [n]
return digits(n/10) + [n%10]
digits(123)
> [1, 2, 3]
The above solution fixes a bug in your code, you were returning the digits in reverse order. Also notice that n must be an integer greater than or equal to zero for producing correct results.
Here's how it works:
If the number is less than 10, then return a list with the number, as there are no more digits to be processed
If the number is greater than 9, get the last digit in the current number and add it to the end of the list that results of recursively calling digits on a smaller number - namely, the number without the last digit that we just processed.
The call to digits(123) will look like this at each step of the recursion:
digits(123) = digits(123/10) + [3]
digits(12) = digits(12/10) + [2]
digits(1) = [1]
Now we go up the call stack:
[1]
[1] + [2]
[1, 2] + [3]
[1, 2, 3]
EDIT :
Accepting #thg435's challenge, here's a tail-recursive solution:
def digits(n):
def loop(i, acc):
if i < 10:
return [i] + acc
return loop(i/10, [i%10] + acc)
return loop(n, [])
When you use recursion, a good basis is to have two cases to check, a base case and a recursive case. The base case is the conditions and result under which the program returns, in your case, the base case would be when n > 0 (If you think of it like a while loop, it's the condition for the while loop exiting). The recursive case (there can be multiple of these) occur when the loop isn't done, if you compare to a while loop, this is basically the body of the loop. At the end of the recursive case, you need to call the function again with your changes to the input, in this case n/10.
So, your function definition would be something like:
def digits(n):
For the base case, you want to check if n is 0, and, if it is, return the empty list:
if n <= 0:
return []
Now, in the recursive case, you want to append n%10 to the list and call your function again, only you want to call it with a different n, changed as you had it in your while loop:
else:
return [n%10]+digits(n/10)
So, if you trace this through, for every recursive case, you get a list containing n%10, then it adds the result of the new call, which will be either (n/10)%10 or the empty list. For example, running this function with n=100 would break down like this:
newlist = digits(100)
newlist = [100%10]+digits(100/10)
newlist = [100%10]+([10%10] + digits(10/10))
newlist = [100%10]+([10%10] + ([1%10] + digits(10/10)))
newlist = [100%10]+([10%10] + ([1%10] + ([])))
newlist = [0,0,1]
Nested parens are used to show how the function digits gets rewritten inline.
def digits(n):
res = []
res.append(n%10)
n /= 10
if n != 0:
return res + digits(n)
else:
return res

Categories

Resources