How to check every element of list recursively? - python

I am currently trying to code a function recursively but I am stuck on the base case. Basically, my problem is that I don't know how to create the base case since I don't know how to check every element of the list recursively. If I was to use a for loop, it would go something like
for i in range(len(A)):
where A is a list. How would I changed that into recursion?
Edit: I am now aware that I gave too little details. Lets say I have a list of numbers [5,6,-1,7,-3] I want my output to be just the positive numbers, so by the code it would be [5,6,3]. Now my problem is that I can easily do this using a for loop to check every element of the list, but I want to do it recursively and I don't know how to do that for lists. I hope It is clearer now/makes sense.

The base case is when then list element is not a nested list, like follows:
def visit_list_rec(x):
# base case, we found a non-list element
if type(x) != list:
print(x)
return
# recursive step, visit each child
for element in x:
visit_list_rec(element)
if __name__ == "__main__":
visit_list_rec(["this", "is", ["a", ["nested", "list"]]])

This seems to work. The base case is when there are no more numbers left to check.
def positive_vals(nums, result=None):
if result is None:
result = []
if not nums:
return result
else:
if nums[0] > 0:
result.append(nums[0])
return positive_vals(nums[1:], result)
numbers = [5, 6, -1, 7, -3]
print(positive_vals(numbers)) # -> [5, 6, 7]

Related

List not returning the proper values as an output

I am trying to write a small function (as an exercise). This function takes a list of values and returns the values that are odd numbers. I have gotten the function to give me the right answer with the print() function, but I am not able to do the same with a return statement.
def odd_nr(list1):
i = 0
for list1[i] in list1:
if list1[i] % 2 != 0:
print(list1[i])
i += 1
return list1
odd_nr([1,2,3,4,5,6])
The output is:
1
3
5
[1, 3, 5, 6, 5, 6]
I am not able to figure out why the return statement gives this output. I tried different indentations, I tried different variants of the return statement, but I just can't seem to get it to work.
What am I doing wrong?
try:
def odd_nr(list1):
results = []
for number in list1:
if number % 2 != 0:
print(number)
results.append(number)
return results
odd_nr([1,2,3,4,5,6])
Further Explanation:
Any function can take something and return something. This something can also be nothing, None.
Your function takes a list, and returns a list, but it is returning the same list that it is taking in.
The print statement is not a return value. Which means a print is not something that the function returns, it is a side-effect, a side door, for us humans to see, mostly.
To return a list of only odd numbers, you need to collect them in another list as you iterate through your original input list.
Then once you are done, return the list that has collected all the odd numbers. Hope this helps.
I also updated your code a bit, for list1[i] in list1, even though it works, it is hard to understand and it does so for the wrong reasons, see below. You can simply do for number in list1 and also not worry about incrementing any counters(i in your case).
Explanation on why for list1[i] in list works:
This is interesting. By choosing list1[i] as the current iteratee, we will be mutating our list while iterating; it is lucky that each iteratee is equal, in value to the list element it is mutating. Here is an example to illustrate what is happening. It is easy to see when we do not update i:
list1= [1,2,3,4]
i=0
for list1[i] in list1:
print(list1[i])
print(list1)
Output:
1
2
3
4
[4, 2, 3, 4]
You just need to return list1[:i] instead of returning the whole list1. Demo with that:
>>> odd_nr([1,2,3,4,5,6])
1
3
5
[1, 3, 5]
With your unusual but correct for list1[i] in list1 and the according update of i, you're moving all the odd numbers to the front of the list and counting them with i. All that's left to do is to only return that front portion.
Alternatively, delete the unwanted back portion with del list1[i:] before you do return list1.

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]

sub-sum from a list without loops

So i'm studying recursion and have to write some codes using no loops
For a part of my code I want to check if I can sum up a subset of a list to a specific number, and if so return the indexes of those numbers on the list.
For example, if the list is [5,40,20,20,20] and i send it with the number 60, i want my output to be [1,2] since 40+20=60.
In case I can't get to the number, the output should be an empty list.
I started with
def find_sum(num,lst,i,sub_lst_sum,index_lst):
if num == sub_lst_sum:
return index_lst
if i == len(sum): ## finished going over the list without getting to the sum
return []
if sub_lst_sum+lst[i] > num:
return find_sum(num,lst,i+1,sub_lst_sum,index_lst)
return ?..
index_lst = find_sum(60,[5,40,20,20,20],0,0,[])
num is the number i want to sum up to,
lst is the list of numbers
the last return should go over both the option that I count the current number in the list and not counting it.. (otherwise in the example it will take the five and there will be no solution).
I'm not sure how to do this..
Here's a hint. Perhaps the simplest way to go about it is to consider the following inductive reasoning to guide your recursion.
If
index_list = find_sum(num,lst,i+1)
Then
index_list = find_sum(num,lst,i)
That is, if a list of indices can be use to construct a sum num using elements from position i+1 onwards, then it is also a solution when using elements from position i onwards. That much should be clear. The second piece of inductive reasoning is,
If
index_list = find_sum(num-lst[i],lst,i+1)
Then
[i]+index_list = find_sum(num,lst,i)
That is, if a list of indices can be used to return a sum num-lst[i] using elements from position i+1 onwards, then you can use it to build a list of indices whose respective elements sum is num by appending i.
These two bits of inductive reasoning can be translated into two recursive calls to solve the problem. Also the first one I wrote should be used for the second recursive call and not the first (question: why?).
Also you might want to rethink using empty list for the base case where there is no solution. That can work, but your returning as a solution a list that is not a solution. In python I think None would be a the standard idiomatic choice (but you might want to double check that with someone more well-versed in python than me).
Fill in the blanks
def find_sum(num,lst,i):
if num == 0 :
return []
elif i == len(lst) :
return None
else :
ixs = find_sum(???,lst,i+1)
if ixs != None :
return ???
else :
return find_sum(???,lst,i+1)

Remove a list while iterating in python without copying to new list

I want to remove elements from my list while iterating the list. I don't think copying the list and performing the operations on either one will solve my problem.
I have a nested list,
Here as soon as I get the leftmost or the rightmost values of the list == maximum I append it to a new list Lst1 and pop the element from the original list else break from the loop.
lst= [[4, 3, 2, 1, 3, 5]]
lst1=[]
for i in range(len(lst)):
if lst[0][i]==max(lst[0]):
lst1.append(lst[0][i])
lst.remove(lst[0][i])
elif lst[0][maxsize_lst-1]==max(lst[0]):
lst1.append(lst[0][maxsize_lst-1])
lst.remove(lst[0][maxsize_lst-1])
else :
print("NO")
break;
I'm getting the below errors and sometimes I get index out of range probably because i'm removing the element and iterating the list again
ValueError: list.remove(x): x not in list
The output of list 1 should look something like:
5 4 3 3 2 1
EDIT
The final list is coming in the descending order but it's not a sorting problem. Here i will be first picking either the leftmost or rightmost element first and check if it is == max(lst). if any of them follows true i'm removing that element.Now my list would be having one less element. If it was leftmost pop then i would resume from index 1 to last ,vice versa if it was rightmost i would again do the same search from index 0 to maxsize-2 to do the same search. If nothing follows like leftmost or rightmost != Max(lst) then break and print No
There is a much simple solution:
lst = [[4, 3, 2, 1, 3, 5]]
print(sorted(lst[0], reverse=True))
Result:
[5, 4, 3, 3, 2, 1]
First of all, you want to remove values from your nested list at the level where they are: lst.remove(x) only searches in lst, not in lst and all possible lists nested in lst. That will solve your problem with ValueError.
Second, simply running your example by hand tells you why it isn't working: you are never updating maxsize_lst, therefore as soon as you pop out an item this value is no longer valid. A simple solution would be to use python's negative indexing system to access the last value of your list: lst[-1]. But even then, if your goal is to get all values in your list sorted, your code cannot do it: on the first step of your example already,
with i=0, you remove 5 from the list (last item, max of values)
next step, i=1, and you will never again access the value at i=0
But then maybe that's not a problem for you, it isn't clear what you want to achieve with your code...
Edit: I re-read your question, if what you want is actually to pop the left/rightmost value when it is a maximum from your old list to your new list, then you shouldn't be iterating over your list with a for loop but rather using a while loop like that:
size_lst = len(lst[0])
while size_lst > 0:
if lst[0][0] == max(lst[0]):
# Leftmost element max of the list
lst1.append(lst[0].pop(0) # Pop leftmost element of lst[0] into lst1
size_lst -= 1 # Record that you decreased the size of your list
elif lst[0][-1] == max(lst[0]):
# Same with the rightmost element
lst1.append(lst[0].pop(-1)
size_lst -= 1
else:
break
It looks like you're sorting the first list. This can be achieved much more easily. The sorted function will automatically sort it from least to greatest, and then you can use the reversed function to sort greatest to least. Try:
lst1 = reversed(sorted(lst[0]))
EDIT: If you need to use the method put forward in the original code, I caught a mistake in your for loop. You are taking the length of lst, not the sublist, the code should be the following:
for i in range(len(lst[0])):
Also, I don't know if you established a variable maxsize_list, but you can get the last element of the list easily with lst[0][-1]. Finally, your error is being caused by you trying to remove lst[0][-1] from lst, not lst[0]. This is your code without syntax errors. I believe there is a symantic error that occurs when the maximum is at the end.
lst= [[4,3,2,1,3,5]]
lst1=[]
for i in range(len(lst[0])):
if lst[0][i]==max(lst[0]):
lst1.append(lst[0][i])
lst[0].remove(lst[0][i])
elif lst[0][-1]==max(lst[0]):
lst1.append(lst[0][-1])
lst[0].remove(lst[0][-1])
else :
print("NO")
break;
#PRMoureu's comment is a big hint to the answer about what's going wrong.
In your sample data, your list is of size 6. You're iterating over the indices, so from 0 through 5. As you progress through your loop, you remove things from your list, but then you continue to look for it. So at some point, you look at lst[0][i] for an i that no longer exists, and that's why you get your error. This will happen as long as you're using the index.
But you don't need the index into the list. You need the value at it. So the recommendation is a very good idea: simply iterate on the list itself, instead of on its indices. This will give you code like this:
lst= [[4, 3, 2, 1, 3, 5]]
lst1=[]
for val in lst[0]:
print(val)
if val == max(lst[0]):
print("a")
lst1.append(val)
lst[0].remove(val)
print(lst[0])
# this shouldn't even be necessary; you should be able to sort just as well without it
elif lst[0][-1]==max(lst[0]):
print("b")
lst1.append(lst[0][-1])
lst[0].remove(lst[0][-1])
else :
print("NO")
break;
Note, python wouldn't use a construct like maxsize_lst. Instead, it would just use lst[0][-1] to get the last element. I fixed the several places where you were referring to lst instead of lst[0], and made your lst definition actually be valid by putting commas between the values.
When I run this code, I get "NO" as a result. Leave the print statements in to understand why. The first time, you have a 4. It isn't the max, so you look to see if the last value is a max. It is, so it gets added. The second time, you have a three, which is again not your max. Nor is the last remaining value (the other 3), so it says "NO" and aborts. You've got the idea by using a break statement, but you need another loop around it that would continue until your list is empty.
To get this to work, you need an outer loop similar to as follows:
lst= [[4, 3, 2, 1, 3, 5]]
lst1=[]
reset = True
while len(lst[0]) != 0 and reset:
print(lst[0], lst1)
reset = False
for val in lst[0]:
print(val)
if val == max(lst[0]):
print("a")
lst1.append(val)
lst[0].remove(val)
reset = True
break
elif lst[0][-1]==max(lst[0]):
print("b")
lst1.append(lst[0][-1])
lst[0].remove(lst[0][-1])
reset = True
break
else :
print("NO")
break
Note that I did need to add a break even when popping from the left side. Without that, the end result was that lst1 had a value of [5, 4, 3, 3, 2], and lst[0] still had [1] in it.

Trying to return numbers in a list

I have a few questions about this code here. What I'm trying to do is write a function that takes 2 inputs, a list and an option, which the option is either 0 or 1, and returns a list of numbers in the list. If the option is 0, it will return numbers that are greater than 5 or less than -5. If the option is 1, it will return a list of all the odd numbers on the first list. This is what I have for code right now:
def splitList(myList, option):
nList = []
for element in range(0,len(myList)):
if option == 0:
if myList[element] > 5:
nList.append(element)
return nList
Right now I got it to return a list of if the elements are greater than 5, but it returns where they are in the list, not the actually value. Say I ran the program
splitList([-6,4,7,8,3], 0)
it would return
[2, 3]
I want it too return the values of 7 and 8 and also -6 but I know I don't have the right code to return -6 as of now. Can someone guide me in the right direction. Also, I want to be using a for loop here. Also I have no clue how to return odd numbers if the option is 1.
Here is my code which works:
def splitList(myList, option):
nList = []
for element in myList:
if option == 0:
if abs(element) > 5:
nList.append(element)
elif option == 1:
if element % 2:
nList.append(element)
return nList
How would I be able to switch this to a while loop?
I tried the following code but it does not seem to work:
def splitList2(myList, option):
nList = []
element = 0
while element < len(myList):
if option == 0:
if abs(element) > 5:
nList.append(element)
elif option == 1:
if element % 2:
nList.append(element)
element = element + 1
return nList
Despite naming your variable element, it's actually the index, not the element at that index.
You can tell that because you have to use myList[element] to compare it.
So, to fix it, do the same thing again:
nList.append(myList[element])
However, there's a much simpler way to do this: Just loop over the elements directly.
nList = []
for element in nList:
if option == 0:
if element > 5:
nList.append(element)
return nList
You almost never want to loop over range(len(spam)). Usually, you just want the elements, so just loop over spam itself. Sometimes you need the indexes and the elements, so loop over enumerate(spam). If you really just need the indexes… step back and make sure you really do (often people think they want this only because they don't know about zip, or because they're trying to make changes in-place instead of copying, but doing it in a way that won't work).
Or, even more simply:
if option != 0:
return []
return [element for element in nList if element > 5]
Meanwhile:
I want it too return the values of 7 and 8 and also -6 but I know I don't have the right code to return -6 as of now.
You can translate your English directly into Python:
it will return numbers that are greater than 5 or less than -5
… is:
… element > 5 or element < -5 …
However, there's a way to write this that's simpler, if you understand it:
… abs(element) > 5 …
So, this gets option 0 to work. What about option 1?
One simple way to tell if a number is odd is if number % 2 is non-zero.
So, let's put it all together:
if option == 0:
return [element for element in nList if abs(element) > 5]
elif option == 1:
return [element for element in nList if element % 2]
else:
raise ValueError("I don't know option {}".format(option))
From a comment:
How would I change this to a while loop?
To change a for loop into a while loop, you have to break it into three parts: initialize the loop variable, write a while test, and update the loop variable inside the body. The general translation is this:
for element in iterable:
spam(element)
it = iterator(iterable)
while True:
try:
element = next(it)
except StopIteration:
break
else:
spam(element)
Ugly, isn't it? But usually, you can come up with something simpler that's specific to your case. For example, if the iterable is a sequence, list a list, you can do this:
index, size = 0, len(sequence)
while index < size:
spam(sequence[index])
index += 1
Still not nearly as nice as the for loop, but not nearly as ugly as the generic while.
Finally, just for fun. Everyone knows that function mappings are more Pythonic than elif chains, right? To prove the value of dogmatically following rules like that, let's do it here:
preds = {0: lambda x: abs(x) > 5,
1: lambda x: x % 2}
def splitList(myList, option):
return filter(preds[option], myList)
Seems like you should just write two separate functions, since the function you're trying to add options to does rather different things.
Python lets you iterate over lists and other data structures easily:
for element in myList:
if option == 0:
if element > 5:
nList.append(element)
....
Because one-liners are fun:
def splitlist(li, flag):
return [x for x in li if x%2==1] if flag else [x for x in li if abs(x)>5]
If this is homework, you probably don't want to be turning in this for an answer, but it should give some ideas.
Other aspects of the question have been ably answered, but there's a rather unpythonic construction in your argument use. Better would be:
def extract_elements(my_list, odd_only):
"""Return select elements from my_list.
If odd_only is True return the odd elements.
If odd_only is False return elements between -5 and 5 inclusive.
"""
…
There are four significant points demonstrated here:
Names are very important, odd_only is far more descriptive than option, and calling a method splitList when it doesn't split anything is confusing to read.
Don't use arbitrary integers to represent a boolean option when the language has intrinsic booleans.
There is no name for that method that could possibly allow the reader to understand its highly idiosyncratic function (and extract_odd_or_magnitude_of_five is hard to type and still isn't descriptive). That's why there are docstrings, they bind the description of the method very closely to the method definition.
Convention matters. The Style Guide for Python helps others read your code.

Categories

Resources