Reverse sign function returns none [duplicate] - python

This question already has answers here:
Why is "None" printed after my function's output?
(7 answers)
Closed 4 years ago.
I would like to create a function where one passes a list of numbers and gets back another list where the signs of all numbers are reversed. This is my code:
def reverse_sign_of_nos_in_a_list(list1):
""" This function reverses sign of numbers
in a list and returns a list.
"""
list2 = []
for num in list1 :
if num > 0 :
return list2.append(num * -1)
elif num < 0 :
return list2.append(abs(num))
else :
return list2.append(num)
print (reverse_sign_of_nos_in_a_list([1,2,3,-1,-2,-3,0]))
The above code shows None as the output. What's the issue?

The are a couple of things wrong with your code:
You have a return statement in every branch of your code. That means that you will return from the first iteration of the loop no matter what.
list.append modifies the target instance in place. As is conventional in Python for such methods, it returns None, which, combined with #1 means that you always get a return value of None.
There's also something very strange about your code. You flip the sign of a positive number by multiplying by -1. That makes sense. But then you take the absolute value of a negative number. Why? Flipping the sign is the same as multiplying by -1 for negative numbers too. And even for zero.
In fact, you don't even need to multiply by -1. There's already a "flip the sign" operator: unary -.
You can write your function as a single list comprehension:
def reverse_sign_of_nos_in_a_list(list1):
return [-x for x in list1]

list.append() method will append the new element in-place and then will return None. So your function returns that None. Also it will return immediatelly after first element of the original list is processed.
def reverse_sign_of_nos_in_a_list(list1):
""" This function reverses sign of numbers
in a list and returns a list.
"""
list2 = []
for num in list1:
if num > 0:
list2.append(num * -1)
elif num < 0:
list2.append(abs(num))
else:
list2.append(num)
return list2
print(reverse_sign_of_nos_in_a_list([1,2,3,-1,-2,-3,0]))
Please, note I keep the code as close as possible to the original code. I would implement the function differently.

reverse_sign_of_nos_in_a_list is returning None because list.append() returns None, not appended list.
Following should do the job:
def reverse_sign_of_nos_in_a_list(list1) :
""" This function reverses sign of numbers
in a list and returns a list.
"""
list2 = []
for num in list1 :
if num > 0 :
list2.append(num * -1)
elif num < 0 :
list2.append(abs(num))
else :
list2.append(num)
return list2

You need to append and then return the list.
list2.append(...)
return list2
The reason you are getting None as output is because append is a void-Method that does not return a value. It only alters list2.

If you want every element of your list reversed, you should remove all the return which cause the function to return prematurely.
Code
def reverse_sign_of_nos_in_a_list(list1) :
""" This function reverses sign of numbers
in a list and returns a list.
"""
list2 = []
for num in list1 :
if num > 0 :
list2.append(num * -1)
elif num < 0 :
list2.append(abs(num))
else :
list2.append(num)
return list2
print (reverse_sign_of_nos_in_a_list([1,2,3,-1,-2,-3,0]))

Because list.append(elem) -- adds a single element to the end of the list. Common error: does not return the new list, just modifies the original
You should use similar to:
def reverse_sign_of_nos_in_a_list(list1) :
""" This function reverses sign of numbers
in a list and returns a list.
"""
list2 = []
for num in list1 :
if num > 0 :
list2.append(-num)
elif num < 0 :
list2.append(abs(num))
else:
list2.append(num)
return list2
print(reverse_sign_of_nos_in_a_list([1,2,3,-1,-2,-3,0]))

A different, shorter (and kind of more pythonic) way for the whole thing:
def reverse_sign_of_nos_in_a_list(list1):
return [(-1)*ii for ii in list1]

Related

How do I append things on list through recursion?

I have this code:
def positive(A):
if len(A)==0:
return 0
else:
if A[0]>0:
return A [0]+positive(A[1:])
else:
return positive(A[1:])
Theoretically, when I input a list of numbers, it will return the list of positive numbers. For example, when I pass [2,5,-3,-5,2,-6] it will return [2,5,2]. But in my code, what happens is that the positive values are evaluated so this returns 9. I think my problem is that in A [0]+positive(A[1:]) line but I don't know how to change it. Any help is appreciated.
Your code should return a list for every case,
so for the empty list case do return [] and if not A to test for empty list
use A[:1] to get a list of one element instead of A[0] which is an int ( [A[0]] does same)
simplified a bit the else, you don't need them if the previous branches does return
def positive(A):
if not A:
return []
if A[0] > 0:
return A[:1] + positive(A[1:])
return positive(A[1:])
A nice idea from the comments is to use the boolean condition directly as the index to slice with
if A[0]>0 is True, then int(True) == 1 so your slice A[:1]
if A[0]>0 is False, then int(False) == 0 so your slice A[:0] which is empty
def positive(A):
if not A:
return []
return A[:int(A[0] > 0)] + positive(A[1:])
With inline if
def positive(A):
return A[:int(A[0] > 0)] + positive(A[1:]) if A else []

Python - Removing first two occurrences of element in list

The objective of this function is to remove the first two occurrences of n in a list.
Below is a code I had written but I still got it wrong after many hours. A friend advised me not to edit a list while iterating. However, I'm still stuck.
def remove_first_two(list,n):
if list == []:
return []
else:
count = 0
for ele in list:
if ele == n:
list.remove(ele)
count += 1
if count == 2:
break
return list
list = [1,2,2,3]
print(remove_first_two(list,2)) => [1,2,3] instead of [1,3]
Use list.remove twice with try-except. That will delete first two entries. Complexity O(n)
list_a = [1,2,3,4]
try:
list_a.remove(n)
list_a.remove(n)
# run a loop too, if it's more than 2
except:
pass
You can try find all indexes and del:
a = [1,2,3,2,3,2,4]
indices = [i for i, x in enumerate(a) if x == 2]
print(indices)
[1, 3, 5]
del a[indices[0]], a[indices[1]]
print(a)
[1, 3, 2, 2, 4]
First, don't use 'list' as its a key word in Python. Use something else, like 'alist'.
The code below does what you want and keeps the basic form of what you already have. You can of course also use the built-in .remove() method.
def remove_first_two(alist, n):
if alist == []:
return []
else:
count = 0
while count < 2:
for ele in alist:
if ele == n:
alist.remove(ele)
count += 1
return alist
alist = [1,2,2,3]
print(remove_first_two(alist,2)) # Output -> [1,3]
When your friend says "do not edit a list while iterating," he/she is right, and what he/she means is that you should create another list all together. What you are looking to do is the following:
def remove_first_two(list, n):
if list == []:
return []
else:
new_list = []
count = 0
for ele in list:
if ele == n:
if count >= 2:
new_list.append(ele)
count += 1
else:
new_list.append(ele)
return new_list
However, note that you can use use some built in functions to make your life much easier:
list.remove(x)
Remove the first item from the list whose value is equal to x. It raises a ValueError if there is no such item.
Therefore, you can more simply do:
def remove_first_two(list, n):
if list == []:
return []
for _ in range(2):
if n in list:
list.remove(n)
return list
Python updates the list if you change it while iterating.
In you test case with list = [1,2,2,3] when list[1] is deleted and Python updates list = [1,2,3]. Now Python understands you have iterated till index 1 and continues from index 2 which now contains 3. So Python encounters only one occurance of 2.
So heed your friends advice and do not edit list while iterating :)
Now you can use Python's in-built list.remove(element) to delete first ocuurence of a element. Repeat it 2 times for desired output.
Also O(n) with a single parse.
def remove_first_two(mylist,n):
counter = 0
def myfilter (i):
nonlocal counter,n
if counter > 2:
return True
else:
counter += 1
return (i != n)
return (list(filter(myfilter,mylist)))
This can also be done in python 3.8 using assignment expressions in a list comprehension:
data = [1,2,3,2,3,2,4]
count = 2
num = 2
[x for x in data if x != num or (count:=count-1) < 0]
Results:
[1, 3, 2, 2, 4]
Here is the reason why your program does not work:
When you remove an element, the for loop moves on to the next element, but by "moving on" it is actually skipping the element which now occupies the position of the deleted element. It skips the element right after the one you deleted.
The correct way to iterate over a list while you delete elements is making index progression explicit, by using a while loop instead of a for loop, and not increase the index when you delete an element:
i = 0
while i < len(my_list):
if condition:
my_list.pop(i)
else:
i += 1
However, none of this is necessary in your case! Notice that when you use my_list.remove(ele), you are not providing an index as you would with my_list.pop(i), so Python has to search for the first element that matches ele. Although remove will be slower than pop when used by themselves, here remove allows you not use any loops at all, simply do my_list.remove(n) twice!
Last touch: If your list has less than two elements matching n, one of the two my_list.remove(n) commands would return a ValueError. You can account for this exception, knowing that if it happens, your list is ready and requires no further action.
So the code you need is:
try:
my_list.remove(n)
my_list.remove(n)
except ValueError:
pass

Why does this recursive function exceed maximum length in python?

#finds length of a list recursively, don't understand why it exceeds maximum length, can someone please explain?
def lengtho(xs):
if(xs == None):
return 0
return 1 + lengtho(xs[1:])
lengtho([1,2,3,4,1,4,1,4,1,1])
When the code reaches the end of the list (the base case), xs will be equal to [] (the empty list), not None. Since [][1:] simply returns [], the function will call itself until the stack overflows.
The correct way to write this function in python would be:
def lengthof(xs):
"""Recursively calculate the length of xs.
Pre-condition: xs is a list"""
if (xs == []):
return 0
else:
return 1+ lengthof(xs[1:])
Your base case of recursion will never get to True value, xs == None will be always False if you are talking about lists, probably you want to change it to xs == [] or just if not xs

Python: Function to find out if a tuple contains even numbers?

So what I want is a function that gets a tuple input from a user and then figures out if that tuple contains an even number. I want to know how to do it with a for or while loop. I've tried it but the code doesn't work. I've kind of got something but it's not working:
def ifEven(x):
i = -1
if isinstance(x, tuple):
while i < len(x):
for i in x:
i = i + 1
if x[i] % 2 == 0:
return True
else:
return False
You should read the documentation about for statement in Python: https://docs.python.org/2/tutorial/controlflow.html#for-statements.
Here is a working code:
def ifEven(x):
if isinstance(x, tuple):
for i in x:
if i % 2 == 0:
return True
return False
That being said, it can be rewritten as a one-liner using Python's generator expressions:
def isEven(x):
return any(v % 2 == 0 for v in x)
You indentation looks a bit off but that might just be how you copy/pasted. True is "True" in python not "true" as in your return function which might also be messing you up.
In for loop i itself initializes and start iterating over the tuple. The content of i at each iteration is one of the element from the tuple.
Let's have a deeper look into it. let your tuple be (3, 4, 6, 7)
In first iteration :-
for i in x # i=3
i = i+1 # i = 4
if x[i] %2 == 0 : # x[4] % 2 == 0 => This will give you IndexError, since there is no index 4 in the tuple x(python uses zero based indexing)
So you don't need to increment i separately as for increments the iterating variable upto the end of the sequence provided (in your case it's upto the last element in the tuple x)
And since i posses the element from the tuple itself, so by incrementing it you are incrementing the value in i. And then you are accessing the element with incremented index from the tuple. That is, if tuple element is 3, you are first incrementing it by 1, so it becomes 4, and then you are checking whether the element in 4th index of the tuple is even or not i.e. if x[4] is even or not. But since the tuple that I have considered, is of lengthe 4, so it will throw IndexError
So, here you don't need to initialize i = -1, don't need to use while, no need to increment i inside for loop. Rest solution is given by Selcuk.

recursively remove adjacent duplicates in a list

I looked up and found a close example, but the answer found in this link: Remove adjacent duplicate elements from a list won't run the test cases for this problem. So this is all I have so far:
def remove_dups(thelist):
"""Returns: a COPY of thelist with adjacent duplicates removed.
Example: for thelist = [1,2,2,3,3,3,4,5,1,1,1],
the answer is [1,2,3,4,5,1]
Precondition: thelist is a list of ints"""
i = 1
if len(thelist) == 0:
return []
elif len(thelist) == 1:
return thelist
elif thelist[i] == thelist[i-1]:
del thelist[i]
return remove_dups(thelist[i:])
def test_remove_dups():
assert_equals([], remove_dups([]))
assert_equals([3], remove_dups([3,3]))
assert_equals([4], remove_dups([4]))
assert_equals([5], remove_dups([5, 5]))
assert_equals([1,2,3,4,5,1], remove_dups([1,2,2,3,3,3,4,5,1,1,1]))
# test for whether the code is really returning a copy of the original list
mylist = [3]
assert_equals(False, mylist is remove_dups(mylist))
EDIT while I do understand that the accepted answer linked above using itertools.groupby would work, I think it wouldn't teach me what's wrong with my code & and would defeat the purpose of the exercise if I imported grouby from itertools.
from itertools import groupby
def remove_dups(lst):
return [k for k,items in groupby(lst)]
If you really want a recursive solution, I would suggest something like
def remove_dups(lst):
if lst:
firstval = lst[0]
# find lowest index of val != firstval
for index, value in enumerate(lst):
if value != firstval:
return [firstval] + remove_dups(lst[index:])
# no such value found
return [firstval]
else:
# empty list
return []
Your assertion fails, because in
return thelist
you are returning the same list, and not a copy as specified in the comments.
Try:
return thelist[:]
When using recursion with list it is most of the time a problem of returning a sub-list or part of that list. Which makes the termination case testing for an empty list. And then you have the two cases:
The current value is different from the last one we saw so we want to keep it
The current value is the same as the last one we saw so we discard it and keep iterating on the "rest" of the values.
Which translate in this code:
l = [1,2,2,3,3,3,4,5,1,1,1]
def dedup(values, uniq):
# The list of values is empty our work here is done
if not values:
return uniq
# We add a value in 'uniq' for two reasons:
# 1/ it is empty and we need to start somewhere
# 2/ it is different from the last value that was added
if not uniq or values[0] != uniq[-1]:
uniq.append(values.pop(0))
return dedup(values, uniq)
# We just added the exact same value so we remove it from 'values' and
# move to the next iteration
return dedup(values[1:], uniq)
print dedup(l, []) # output: [1, 2, 3, 4, 5, 1]
problem is with your return statement,
you are returning
return remove_dups(thelist[i:])
output will be always last n single element of list
like for above soon,
print remove_dups([1,2,2,3,3,3,4,5,1,1,1])
>>> [1] #as your desired is [1,2,3,4,5,1]
which returns finally a list of single element as it don't consider Oth element.
here is recursive solution.
def remove_dups(lst):
if len(lst)>1:
if lst[0] != lst[1]:
return [lst[0]] + remove_dups(lst[1:])
del lst[1]
return remove_dups(lst)
else:
return lst

Categories

Resources