Python list recursive changes - python

I have a bug in my attempt to add to a list a sequence of numbers recursively. E.g. if the input is [5,3,9], I do [5+1,3+2,9+3] and output [6,5,12]. I want to do this recursively so the way I'm doing it is going through and adding one to a smaller and smaller part of the list as below:
def add_position_recur(lst, number_from=0):
length = len(lst)
# base case
if (length <= 1):
lst = [x+1 for x in lst]
print "last is", lst
else:
lst = [x+1 for x in lst]
print "current list is", lst
add_position_recur(lst[1:], number_from)
return lst
The problem, though, is that all this does is add 1 to every element of the list. Where is the bug? Is it to do with the way I return the list in the base case?

When you recurse down your call stack you slice lst which creates a new list, this is not the same as what you return, so you will only ever return the changes you've applied to your list in the first call to the function, losing all changes further down the stack:
>>> add_position_recur([1,2,3])
[2, 3, 4]
This should have returned [2, 4, 6].
You need to consider reassembling the list on the way out to get the changes.
return [lst[0]] + add_position_recur(lst[1:], number_from)
and you need to return lst in your base case:
def add_position_recur(lst, number_from=0):
length = len(lst)
# base case
if (length <= 1):
lst = [x+1 for x in lst]
return lst
else:
lst = [x+1 for x in lst]
return [lst[0]] + add_position_recur(lst[1:], number_from)
>>> add_position_recur([1,2,3])
[2, 4, 6]
However, this is quite a complicated approach to this recursion. It is idiomatic for the base case to be the empty list, otherwise take the head and recurse down the tail. So something to consider which uses the number_from:
def add_position_recur(lst, number_from=1):
if not lst:
return lst
return [lst[0]+number_from] + add_position_recur(lst[1:], number_from+1)
>>> add_position_recur([1,2,3])
[2, 4, 6]
This also has the advantage(?) of not changing the passed in lst

Why don't you instead do something like this:
def func(lon, after=[]):
if not l:
pass
else:
v = len(lon) + lon[-1]
after.append(v)
func(lon[:-1], after)
return after[::-1]
The output of the function for the example you provided matches what you want.

Currently, you are simply adding 1 to each value of your list.
lst = [x+1 for x in lst]
Rather, you should be increasing a variable which is being added to x with each iteration of x in lst.
lst = [x+(lst.index(x)+1) for x in lst]
This solution assumes that you want the number being added to x to depend on its position in the list relative to the start of the list, rather than being dependent on the position of x relative to the first element which was >1. Meaning, do you want to add 1 or 3 to the value 2 in the following list? The solution above adds three.
lst = [0.5, 0.1, 2, 3]

Related

remove even elements from a list. Showing list index out of range

image
please refer to the image.
I want to create a list(say [1,2,3,4,5]). The code checks the divisibility by 2 of every element of the list one by one. if it is divisible by 2, it removes the those elements and returns a list of odd numbers . Please try to keep the code as simple as possible as i am just a beginner....
The problem with your code is that once you remove the even number from the list, It reduces the length of the list. So in the second iteration of your code, 2 is removed from the list l = [1,2,3,4,5] which modifies the list l to [1,3,4,5], so now, the length of the list is 4 instead of 5. But the for loop, which will run for the values "0, 1, 2, 3, 4" (because of variable x, which is 5), your code produces that error.
To solve this error, you can create a new list and append the even numbers in it.
It's a lot easier and more Pythonic to build a new list of the odd numbers than it is to modify the original list to remove the even numbers.
If you understand list comprehensions, there's a simple answer.
l = [1, 2, 3, 4, 5]
l2 = [x for x in l if x%2 == 1]
print(l2)
You can also do it imperatively:
l = [1, 2, 3, 4, 5]
l2 = []
for x in l:
if x%2 == 1:
l2.append(x)
print(l2)
Either solution will print [1, 3, 5].
l = [1,2,3,4,5]
x = len(l)
new_l = []
for a in range(x):
if l[a]%2!=0:
new_l.append(l[a])
new_l
Use the above code instead of removing elements from list create a new one.
I took a look at the image and couldn't figure out much. Going by your definition
l = [1,2,3,4]
for i in l:
if i % 2 == 0: # check if i is divisible by 2
l.remove(i) # remove the item if the number passes above check.

how do i adjust my code to become a recursive reverse sum loop

I have a simple recursion method for reversing a list
lst = [1,2,3,4,5] will print as [5, 4, 3, 2, 1]
lst = [1, 2, 3, 4, 5]
def revlist(lst):
if len(lst) == 0:
return []
else:
return revlist(lst[1:]) + [lst[0]]
print(revlist(lst))
Instead of it printing the reverse list i want it to add 5 + 4 + 3 + 2 + 1 but i dont know how to, at the end I should get 15 but not by just adding the sum of the list, only by adding at the last element and finishing at the first [0] element. Whatever I do to try and sum the array list it gives me errors and I dont really know how I can get any closer to code what I am trying to acheive.
This is a strange requirement, but it could be accomplished like this:
def revsum(lst):
if not lst:
return 0
return lst[-1] + revsum(lst[:-1])
Of course, this is purely academic, and you should never use this in real Python code.
This will work
lst = [1, 2, 3, 4, 5]
print(sum(lst[::-1])
It will reverse the lst using step counter(notice the two colons) then uses inbuilt sum function to add the list.

Updating the range of list in function argument

Problem to solve: Define a Python function remdup(l) that takes a non-empty list of integers l
and removes all duplicates in l, keeping only the last occurrence of each number. For instance:
if we pass this argument then remdup([3,1,3,5]) it should give us a result [1,3,5]
def remdup(l):
for last in reversed(l):
pos=l.index(last)
for search in reversed(l[pos]):
if search==last:
l.remove(search)
print(l)
remdup([3,5,7,5,3,7,10])
# intended output [5, 3, 7, 10]
On line 4 for loop I want the reverse function to check for each number excluding index[last] but if I use the way I did in the above code it takes the value at pos, not the index number. How can I solve this
You need to reverse the entire slice, not merely one element:
for search in reversed(l[:pos]):
Note that you will likely run into a problem for modifying a list while iterating. See here
It took me a few minutes to figure out the clunky logic. Instead, you need the rest of the list:
for search in reversed(l[pos+1:]):
Output:
[5, 3, 7, 10]
Your original algorithm could be improved. The nested loop leads to some unnecessary complexity.
Alternatively, you can do this:
def remdup(l):
seen = set()
for i in reversed(l):
if i in seen:
l.remove(i)
else:
seen.add(i)
print(l)
I use the 'seen' set to keep track of the numbers that have already appeared.
However, this would be more efficient:
def remdup(l):
seen = set()
for i in range(len(l)-1, -1, -1):
if l[i] in seen:
del l[i]
else:
seen.add(l[i])
print(l)
In the second algorithm, we are iterating over the list in reverse order using a range, and then we delete any item that already exists in 'seen'. I'm not sure what the implementation of reversed() and remove() is, so I can't say what the exact impact on time/space complexity is. However, it is clear to see exactly what is happening in the second algorithm, so I would say that it is a safer option.
This is a fairly inefficient way of accomplishing this:
def remdup(l):
i = 0
while i < len(l):
v = l[i]
scan = i + 1
while scan < len(l):
if l[scan] == v:
l.remove(v)
scan -= 1
i -= 1
scan += 1
i += 1
l = [3,5,7,5,3,7,10]
remdup(l)
print(l)
It essentially walks through the list (indexed by i). For each element, it scans forward in the list for a match, and for each match it finds, it removes the original element. Since removing an element shifts the indices, it adjusts both its indices accordingly before continuing.
It takes advantage of the built-in the list.remove: "Remove the first item from the list whose value is equal to x."
Here is another solution, iterating backward and popping the index of a previously encountered item:
def remdup(l):
visited= []
for i in range(len(l)-1, -1, -1):
if l[i] in visited:
l.pop(i)
else:
visited.append(l[i])
print(l)
remdup([3,5,7,5,3,7,10])
#[5, 3, 7, 10]
Using dictionary:
def remdup(ar):
d = {}
for i, v in enumerate(ar):
d[v] = i
return [pair[0] for pair in sorted(d.items(), key=lambda x: x[1])]
if __name__ == "__main__":
test_case = [3, 1, 3, 5]
output = remdup(test_case)
expected_output = [1, 3, 5]
assert output == expected_output, f"Error in {test_case}"
test_case = [3, 5, 7, 5, 3, 7, 10]
output = remdup(test_case)
expected_output = [5, 3, 7, 10]
assert output == expected_output, f"Error in {test_case}"
Explanation
Keep the last index of each occurrence of the numbers in a dictionary. So, we store like: dict[number] = last_occurrence
Sort the dictionary by values and use list comprehension to make a new list from the keys of the dictionary.
Along with other right answers, here's one more.
from iteration_utilities import unique_everseen,duplicates
import numpy as np
list1=[3,5,7,5,3,7,10]
dup=np.sort(list((duplicates(list1))))
list2=list1.copy()
for j,i in enumerate(list2):
try:
if dup[j]==i:
list1.remove(dup[j])
except:
break
print(list1)
How about this one-liner: (convert to a function is easy enough for an exercise)
# - one-liner Version
lst = [3,5,7,5,3,7,10]
>>>list(dict.fromkeys(reversed(lst)))[::-1]
# [5, 3, 7, 10]
if you don't want a new list, you can do this instead:
lst[:] = list(dict.fromkeys(reversed(lst)))[::-1]

python for loop with lists

I get:
if(lst[i]%2!=0):
IndexError: list index out of range
from this code:
lst=[1,2,3,4,5,6]
for i in range(len(lst)):
if(lst[i]%2!=0):
lst.remove(lst[i])
print(lst)
I am trying to print only the even numbers in a list and I do not see a problem with my code
why am I getting this error ?
You should not remove from a list while iterating it. Use, for instance, a list comprehension and slice assignment to achieve the same modification:
lst = [1,2,3,4,5,6]
lst[:] = [x for x in lst if x % 2 == 0]
# or simply (if you don't need to mutate the original list)
# lst = [x for x in lst if x % 2 == 0]
print(lst)
# [2, 4, 6]
This has also better time complexity (linear) whereas the repeated remove approach is quadratic.
Instead of removing the item from the original list, you can also split the original list into 2 new lists, evens and odds
lst=[1,2,3,4,5,6]
evens = []
odds = []
for i in lst):
if(i % 2 != 0):
odds.append(i)
else:
evens.append(i)
print(evens)

How to Compare sublists

I'm trying to find the smallest number out of a list made up of sublists.
The output for the program should go like this:
least([[2,4,3],[1,7,9,4]])
in [[2,4,3],[1,7,9,4]] the least number is 1 found in sublist [1,7,9,4]
So far I have code that finds the smallest number in a list and that prints out sublists but how do I combine them together, that's really my issue.
# finds smallest number in list
def test(list1):
x = list1[0]
for i in list1:
if i < x:
x = i
print(x)
# prints out sublists
def test2(num):
for x in num:
for y in x:
print (y, end = " ")
print("")
Does the body of "test" go before or after the line
for y in x:
Python has a built-in min function. But I guess it's a good learning exercise to write your own.
We can write a function to find the sublist containing the minimum element by creating a modified version of your test function.
The key idea is to find the minimum of each sublist, and when we find a new minimum we store the sublist that that minimum came from.
In the code below I've change the function name from test to minimum to make it more meaningful.
def minimum(list1):
''' Finds smallest item in list1 '''
x = list1[0]
for i in list1:
if i < x:
x = i
return x
def least(list2d):
minseq = list2d[0]
x = minimum(minseq)
for seq in list2d[1:]:
i = minimum(seq)
if i < x:
x = i
minseq = seq
print('In {} the least number is {} found in sublist {}'.format(list2d, x, minseq))
# Test
data = [[2, 4, 3], [1, 7, 9, 4], [6, 7, 5]]
least(data)
output
In [[2, 4, 3], [1, 7, 9, 4], [6, 7, 5]] the least number is 1 found in sublist [1, 7, 9, 4]
However, we can write this in a more compact way by using the built-in min function to find the smallest sublist for us. The trick here is that we get min to call itself to find the the minimum item in each sublist, and then use those minima to decide which sublist is the minimal one.
def least(list2d):
minseq = min(list2d, key=min)
x = min(minseq)
print('In {} the least number is {} found in sublist {}'.format(list2d, x, minseq))
This version is slightly inefficient since it computes the minimum of the sublist with the smallest item twice. To avoid that we can pass min a generator expression:
def least(list2d):
x, minseq = min((min(seq), seq) for seq in list2d)
print('In {} the least number is {} found in sublist {}'.format(list2d, x, minseq))
That generator expression creates tuples of each sublist and its minimum, those tuples are then passed to the outer min call in order to find the tuple containing the smallest minimum. If 2 or more tuples tie for the minimum then the tuples themselves are compared to decide on the winner.
Assuming that sub lists are only one level deep you can do this by maintaining a variable containing the least value seen, and another to keep track of the list that contained that value:
lists = [[2, 4, 3], [1, 7, 9, 4]]
min_list = None
min_value = lists[0][0] # initialse to the first item of the first list
for sublist in lists:
min_ = min(sublist)
if min_ < min_value:
min_value = min_
min_list = sublist
print("the least number is {} found in sublist {}".format(min_value, min_list))
Short and simple:
>>> lst = [[2, 4, 3], [1, 7, 9, 4]]
>>> min_value_in_lst = min(min(sublist) for sublist in lst)
1
If you want to know where that min value came from, just simply create a loop:
for sublist in lst:
if min_value_in_lst in sublist:
return sublist
The full function:
def least(lst):
min_value_in_lst = min(min(sublist) for sublist in lst)
that_sublist = None
for sublist in lst:
if min_value_in_lst in sublist:
that_sublist = sublist
break
# print out the result
If you need pythonic way to deal, then try this simple code:-
a = [[2,4,3],[1,7,9,4]]
min(reduce(lambda x,y : x+y, a))
Output:-
1

Categories

Resources