Recursively going through a list (python) - python

Say I have a list x = [1, 2, 3, 4]
Is there a recursive method where i can go through the list to find the value?
I want to ultimately be able to compare a returned value in the list, (or nested list) to an arbitrary number to see it it matches.
I can think a way to do this using a for loop, but i have trouble imagining a recursive method to do the same thing. I know that I can't set a counter to keep track of my position in the list because calling the function recursively would just reset the counter every time.
I was thinking I could set my base case of the function as a comparison between the number and a list of len 1.
I just want some hints really.

This is not the way to do things in Python, but surely - you can traverse a list of lists recursively:
def findList(lst, ele):
if not lst: # base case: the list is empty
return False
elif lst[0] == ele: # check if current element is the one we're looking
return True
elif not isinstance(lst[0], list): # if current element is not a list
return findList(lst[1:], ele)
else: # if current element is a list
return findList(lst[0], ele) or findList(lst[1:], ele)

Recursive functions are idiomatic for when you have a linked list. Python lists are more like arrays. But it's still possible to handle a Python list with a recursive function -- there's no real utility, but it can be interesting as an exercise.
You start with a full list, and your base case is when the list is empty. Traverse the list by passing the list in as an argument, using x.pop() to simultaneously fetch and remove the first item in the list, evaluate the popped item, and then pass the list (now shorter) into the same function.
Edit: actually, on second thought, you would be better off not using x.pop() and instead peeking at the first value and passing the remainder in a slice. This would be grossly inefficient, because you're copying the list every time you slice, but it's better than destructively consuming the list inside your recursive function, unless that's a desired side-effect.

Well you will have two base cases:
1) You have reached the end of the list => return false.
2) Your current element is the element you are looking for => return true (or the element or its position, whatever you are interested in).
The thing you have to do all the time is check both base cases on the current element and apply the function recursively on the next element in the list if neither one of the base cases applied.

Related

double list in return statement. need explanation in python

So I was trying to complete this kata on code wars and I ran across an interesting solution. The kata states:
"Given an array of integers, find the one that appears an odd number of times.
There will always be only one integer that appears an odd number of times."
and one of the solutions for it was:
def find_it(seq):
return [x for x in seq if seq.count(x) % 2][0]
My question is why is there [0] at the end of the statement. I tried playing around with it and putting [1] instead and when testing, it passed some tests but not others with no obvious pattern.
Any explanation will be greatly appreciated.
The first brackets are a list comprehension, the second is indexing the resulting list. It's equivalent to:
def find_it(seq):
thelist = [x for x in seq if seq.count(x) % 2]
return thelist[0]
The code is actually pretty inefficient, because it builds the whole list just to get the first value that passed the test. It could be implemented much more efficiently with next + a generator expression (like a listcomp, but lazy, with the values produced exactly once, and only on demand):
def find_it(seq):
return next(x for x in seq if seq.count(x) % 2)
which would behave the same, with the only difference being that the exception raised if no values passed the test would be IndexError in the original code, and StopIteration in the new code, and it would operate more efficiently by stopping the search the instant a value passed the test.
Really, you should just give up on using the .count method and count all the elements in a single pass, which is truly O(n) (count solutions can't be, because count itself is O(n) and must be called a number of times roughly proportionate to the input size; even if you dedupe it, in the worst case scenario all elements appear twice and you have to call count n / 2 times):
from collections import Counter
def find_it(it):
# Counter(it) counts all items of any iterable, not just sequence,
# in a single pass, and since 3.6, it's insertion order preserving,
# so you can just iterate the items of the result and find the first
# hit cheaply
return next(x for x, cnt in Counter(it).items() if cnt % 2)
That list comprehension yields a sequence of values that occur an odd number of times. The first value of that sequence will occur an odd number of times. Therefore, getting the first value of that sequence (via [0]) gets you a value that occurs an odd number of times.
Happy coding!
That code [x for x in seq if seq.count(x) % 2] return the list which has 1 value appears in input list an odd numbers of times.
So, to make the output as number, not as list, he indicates 0th index, so it returns 0th index of list with one value.
There is a nice another answer here by ShadowRanger, so I won't duplicate it providing partially only another phrasing of the same.
The expression [some_content][0] is not a double list. It is a way to get elements out of the list by using indexing. So the second "list" is a syntax for choosing an element of a list by its index (i.e. the position number in the list which begins in Python with zero and not as sometimes intuitively expected with one. So [0] addresses the first element in the list to the left of [0].
['this', 'is', 'a', 'list'][0] <-- this an index of 'this' in the list
print( ['this', 'is', 'a', 'list'][0] )
will print
this
to the stdout.
The intention of the function you are showing in your question is to return a single value and not a list.
So to get the single value out of the list which is built by the list comprehension the index [0] is used. The index guarantees that the return value result is taken out of the list [result] using [result][0] as
[result][0] == result.
The same function could be also written using a loop as follows:
def find_it(seq):
for x in seq:
if seq.count(x) % 2 != 0:
return x
but using a list comprehension instead of a loop makes it in Python mostly more effective considering speed. That is the reason why it sometimes makes sense to use a list comprehension and then unpack the found value(s) out of the list. It will be in most cases faster than an equivalent loop, but ... not in this special case where it will slow things down as mentioned already by ShadowRanger.
It seems that your tested sequences not always have only one single value which occurs an odd number of times. This will explain why you experience that sometimes the index [1] works where it shouldn't because it was stated that the tested seq will contain one and only one such value.
What you experienced looking at the function in your question is a failed attempt to make it more effective by using a list comprehension instead of a loop. The actual improvement can be achieved but by using a generator expression and another way of counting as shown in the answer by ShadowRanger:
from collections import Counter
def find_it(it):
return next(x for x, cnt in Counter(it).items() if cnt % 2)

Write a function longest, which also takes a list ll of lists. It should return (a reference to) the longest of the lists in ll

If there are ties on length,then it should return the earliest of those lists (i.e., the one with the smallest index in ll). Finally, if ll is empty,then your function should return None, instead of a list.
Again, you should not modify the input list(s) in any way. Furthermore, you should return a (reference to) one of the existing elements of ll (or None), and not create a new list.
This is what I have right now but when I run the tests they fail.
def longest(ll):
if len(ll) == 0:
return None
index = 0
for i in range(1, len(ll)):
if len(ll[i] > len(ll[index])):
Does anyone know how I could fix my code to meet the problem's requirements?
**HINT:
Here, the “accumulator” is an (index of or reference to—either will work, we’ll use the former in specific Python statements below) element of ll. Compared to calculating the maximum value, the only essential difference is that the condition used to update the “accumulator” depends on one of it’s properties (specifically it’s length) rather than on the value of the accumulator itself. Although the term “accumulator” sounds, perhaps, somewhat strange in this setting, it simply means (always) that the update rule for it depends on it’s previous value (as well as, typically, something else). Specifically, the next
value of the list (reference) is either unchanged or set to the current element of ll, depending on whether the length of the latter is greater than the length of the former. In other words, let’s assuming l is variable of the for loop that
iterates over the elements of a list. Then, at the end of the loop, if we want largest to hold (a reference to) the l with the largest value (as determined by > on the element type), then our update rule would be similar to, e.g.,
if l > largest: largest = l (among the several possible ways of writing this). However, if we want largest to eventually hold (a reference to) the l with the largest length, then the update rule would simply change to, e.g., if len(l) > len(largest): largest = l.
You can get the length of all of the lists, then find the highest length, then get the (first) index of that maximum value and return that element of your original list.
def longest(ll):
if len(ll) == 0:
return None
lengths = [len(l) for l in ll]
return ll[lengths.index(max(lengths))]

Does Python create a new list when a sliced list is a parameter in a recursive function?

For the purposes of understanding recursion and its performance in Python I wrote a recursive function that returns the maximum in the list:
def max_list(A):
print(id(A)) # debugging purposes
if len(A) > 1:
max_list_tail = max_list(A[:-1])
else:
max_list_tail = A[0]
if A[-1] > max_list_tail:
return A[-1]
else:
return max_list_tail
The idea behind this function is that if the last element of the current list is greater than the maximum among the 'tail' of the list, which is, all the elements, except for the last one, that the last one is the maximum, otherwise the maximum of the tail is the maximum.
As you can see, I pass a sliced list A[:-1] as a parameter.
Questions:
Am I right saying the following: Each time I pass A[:-1] a new list is created, which is a copy of my old list except for the last element? For debugging purposes I print the id of each sliced list and the ids are different which prompts me to think I'm right but I want confirmation from the expert community.
If 1. is yes, that's true, what is the time complexity of my function? Can I say that it is O(n^2) because it takes O(n) to find the maximum in the initial list and O(n) to create a copy of the current list each time I recursively call the function?

loop with strange return function?

I have a question about the below code, particularly the 6th line; is it right to say that it is returning the index of target-nums[I]? If so, why is there another I after it?
Also, what is comp[nums[I]] = I doing? Is it assigning values of nums into comp if it is not in comp already?
Finally, what is the final return [ ] doing in the last line of code?
def TwoSum(nums, target):
comp = {}
for i in range(len(nums)):
if (target - nums[i]) in comp:
return [comp[target - nums[i]],i]
comp[nums[i]] = i
return []
print(TwoSum(nums,target))
is it right to say that it is returning the index of target-nums[I]?
If so, why is there another 'I' after it?
It is returning a list of two items, the first item being comp[target - nums[i]], and the second item being i. It's the same idea as:
def addAndSubtract(x, y):
return [x+y, x-y]
Above, we return a list, the first item in the list is the value of evaluating x+y and the second value is the result of evaluating x-y.
Also, what is comp[nums[I]] = I doing? Is it assigning values of nums into comp > if it is not in comp already?
This will assign the value of nums[i] as a key in your comp dictionary and assign it the value of i. It essentially stores the current value in nums and along with its index. This does two things:
Allows you to easily and quickly check if you have seen a given number yet by checking if it is a key in your comp dictionary
Allows you to check where that number was last seen in your list.
The comp[nums[i]] = i occurs each time your for loop runs, so it will do it for all numbers, in your list, unless it returns in your if-statement. If you happen to encounter the same number again (which is already in your list), then this assignment will simply overwrite the value with the current index of the current number (ie: i).
Finally, what is the final return [ ] doing in the last line of code?
The purpose of this is to return an empty list. It is just a way to signify that no result was found. You will only reach that return when you have iterated through all the numbers in your list and not returned from within your for loop, thus indicating no sum can be made to reach the target.
I explain how this algorithm works in detail here, so you might want to check that out if you need more of an explanation. Although the question is a JavaScript question, the logic explained is the exact same as this.

Recursively sorting two separate lists

Define a recursive function named sort; it is passed any unordered list; it returns a new list (not mutating the argument) that contains every value in the argument list, but in non-descending order.
For example, calling sort([4,5,3,1,6,7,2]) would call sort recursively on the lists [4,5,3]and [1,6,7,2], returning the lists [3,4,5] and [1,2,6,7] respectively, which when merged would be return the list [1,2,3,4,5,6,7].
Note when the length of the list is even, both sublists will have the same
size; when the length of the list is odd, it is natural to divide it into two lists, the first of which has one fewer values than the second.
The function starts like this:
def sort(l):
I know how to separate l into two list, for example [4,5,3,1,6,7,2], I separate them into [4,5,3] and [1,6,7,2]. But I really don't know how to recursively sorting each half when the sort function only has one argument l.
Can someone please tell the way to do this? Thank you.
I gave it a go and it seems to work. Like TobiasR said, it's a merge sort. If you want a better explanation of how it works, I'd check out this video on merge sorts by Harvard University.
Anyway, this is the code for it:
def sort(l):
if len(l)==2:
if l[0]<l[1]:
return l #return list as it is
return l[::-1] #return reversed list
elif len(l)==1:
return l
l_1=sort(l[:len(l)//2]) # cut the list in half
l_2=sort(l[len(l)//2:])
l_out=[]
while True:
if not l_1: #check l1 isn't empty
l_out+=l_2
break;
elif not l_2: #check l2 isn't empty
l_out+=l_1
break;
if l_1[0]<l_2[0]: #put the smallest item in the list
l_out.append(l_1.pop(0))
else:
l_out.append(l_2.pop(0))
return l_out

Categories

Resources