I have a list of elements. I want to know if there are two pairs of elements in the list, in which the elements of the pair have the same value.
My idea is that I first compare all the elements in the list, if a pair is found, remove the pair from the list, then proceed again. Thus I think I can use recursion to do this task, but limit the depth to 2 to solve the problem.
Here is my first try:
recursion_depth=0
def is_twopair(card):
nonlocal recursion_depth
if recursion_depth==2: return True
for i in range(0, len(card)):
for k in range(i+1,len(card)):
if card[i].value==card[k].value:
del card[k], card[i]
recursion_depth+=1
is_twopair(card)
else: continue
else: continue
else: return False
I use the variable recursion_depth to record the the depth of recursion, but then realize that the return command doesn't immediately terminate the function and return true, but returns to its original caller is_twopair(card) instead. So my question is:
Is there a way to immediately terminate the function and return the result true?
Is there a way to limit the depth of recursion?
I know there maybe several ways to work around this. But I want to stay faithful to my idea and use this as an opportunity for learning.
I don't believe you can "break out" of the recursion without some serious black magic, nor do I believe that you should try to. Cascading the return value up the calling chain is typically a fine approach.
I'd personally avoid using a nonlocal variable and keep track of the recursion depth within the scope of each function call.
def remove_pairs(card, count=2, depth=0):
if depth == count:
return card
for i in range(0, len(card)):
for j in range(i+1, len(card)):
if card[i].value == card[j].value:
del card[j], card[i]
return remove_pairs(card, count, depth+1) # add return here
Moving the tracking of the depth into the function itself will allow the function to be called repeatedly from different locations without issue.
Additionally, the code can be cleaned up some using itertools.combinations.
from itertools import combinations
def remove_pairs(cards, count=2, depth=0):
if depth == count:
return cards
for card1, card2 in combinations(cards, 2):
if card1.value == card2.value:
cards.remove(card1)
cards.remove(card2)
return remove_pairs(cards, count, depth+1)
I think the piece you're missing is a return call to pass on the results of a recursive call back up to the previous caller:
if card[i].value==card[k].value:
del card[k], card[i]
recursion_depth+=1
return is_twopair(card) # add return here!
I don't really think recursion is a natural way to solve this problem, but with the above change, it should work. You could avoid needing to use a nonlocal variable by passing the depth as an optional parameter.
yourList = [1,1,2,2,3,4]
yourDict = {}
for i in yourList:
yourDict[i] = yourList.count(i)
This code will return the number of ocurrences for every value in the list so you can determinate the number of pairs..
In this case:
yourDict - - > {1: 2, 2: 2, 3: 1, 4: 1}
The value 1 appear 2 times, the value 2 appear 2 times, the value 3 and 4 appear 1 time.
Related
could someone help please, i do not know why i get this error:
def sum_list(the_list):
if not the_list:
return 0
else:
mid = len(the_list) // 2
return sum_list(the_list[:mid]) + sum_list(the_list[mid:])
print(sum_list([10, 20, 30]))
if the_list is of length 1 (which will happen at some stage in your recursive calls) you will end up in an infinite recursion... (mid will be 0).
you need to address that as well as a base case:
def sum_list(the_list):
if not the_list:
return 0
if len(the_list) == 1:
return the_list[0]
else:
mid = len(the_list) // 2
return sum_list(the_list[:mid]) + sum_list(the_list[mid:])
i assume this is an exercise in recursion. python offers sum to do that efficiently.
Solution
You get an infinite recursion for a list of length 1, because the_list[1 // 2:] would return the_list[0:] which is the same list
Some background on the error
The cost of calling a function is a creation of a new frame (call stack).
There is a soft limit in Python which prevents it from creating too many frames, and this limit is 1000 by default.
You can raise this limit, but it's highly discouraged because it can cause interpreter crash on higher values.
I've taken your function and added some print statements that help show what the problem is.
def sum_list(the_list, depth=1):
print(" The list: ", the_list, "at depth: ", depth)
if not the_list:
print(" List empty at depth", depth)
return 0
else:
mid = len(the_list) // 2
print(" Splitting list at ", depth, "into ", the_list[:mid], " and ", the_list[mid:])
return sum_list(the_list[:mid], depth +1) + sum_list(the_list[mid:], depth + 1)
print(sum_list([10, 20, 30]))
If you run this code, you'll be able to see the value of the_list at each call to sum_list() which can help show why the recursion is infinite.
When trying to debug a function, it can sometimes be useful to stick a print statement in there to see how it's being called. We expect that this function should eventually return because it will eventually reduce to the base case, which is an empty list. Does that happen?
def sum_list(the_list):
print(f"Summing {the_list}")
...
prints:
...
Summing [10]
Summing []
Summing [10]
Summing []
Summing [10]
Summing []
...
RecursionError: maximum recursion depth exceeded while calling a Python object
This tells us that something might be wrong with summing a list that's 1 item long. Let's step through that logic step by step:
>>> the_list = [10]
>>> mid = len(the_list) // 2
>>> the_list[:mid]
[]
>>> the_list[mid:]
[10]
So when we do our recursion on [10], we're going to end up making another recursive call to [10] again, which will just repeat infinitely. To fix this we need to extend our base case:
if len(the_list) == 1:
return the_list[0]
Note that if our base case only ever returned zero, it'd be impossible for the recursive call to ever return anything that wasn't a sum of zeroes!
I've written a function to create combinations of inputs of an arbitrary length, so recursion seemed to be the obvious way to do it. While it's OK for a small toy example to return a list of the results, I'd like to yield them instead. I've read about yield from, but don't fully understand how it is used, the examples don't appear to cover my use case, and hoping'n'poking it into my code has not yet produced anything that works. Note that writing this recursive code was at the limit of my python ability, hence the copious debug print statements.
This is the working list return code, with my hopeful non-working yield commented out.
def allposs(elements, output_length):
"""
return all zero insertion paddings of elements up to output_length maintaining order
elements - an iterable of length >= 1
output_length >= len(elements)
for instance allposs((3,1), 4) returns
[[3,1,0,0], [3,0,1,0], [3,0,0,1], [0,3,1,0], [0,3,0,1], [0,0,3,1]]
"""
output_list = []
def place_nth_element(nth, start_at, output_so_far):
# print('entering place_nth_element with nth =', nth,
# ', start_at =', start_at,
# ', output_so_far =', output_so_far)
last_pos = output_length - len(elements) + nth
# print('iterating over range',start_at, 'to', last_pos+1)
for pos in range(start_at, last_pos+1):
output = list(output_so_far)
# print('placing', elements[nth], 'at position', pos)
output[pos] = elements[nth]
if nth == len(elements)-1:
# print('appending output', output)
output_list.append(output)
# yield output
else:
# print('making recursive call')
place_nth_element(nth+1, pos+1, output)
place_nth_element(0, 0, [0]*output_length)
return output_list
if __name__=='__main__':
for q in allposs((3,1), 4):
print(q)
What is the syntax to use yield from to get my list generated a combination at a time?
Recursive generators are a powerful tool and I'm glad you're putting in the effort to study them.
What is the syntax to use yield from to get my list generated a combination at a time?
You put yield from in front of the expression from which results should be yielded; in your case, the recursive call. Thus: yield from place_nth_element(nth+1, pos+1, output). The idea is that each result from the recursively-called generator is iterated over (behind the scenes) and yielded at this point in the process.
Note that for this to work:
You need to yield the individual results at the base level of the recursion
To "collect" the results from the resulting generator, you need to iterate over the result from the top-level call. Fortunately, iteration is built-in in a lot of places; for example, you can just call list and it will iterate for you.
Rather than nesting the recursive generator inside a wrapper function, I prefer to write it as a separate helper function. Since there is no longer a need to access output_list from the recursion, there is no need to form a closure; and flat is better than nested as they say. This does, however, mean that we need to pass elements through the recursion. We don't need to pass output_length because we can recompute it (the length of output_so_far is constant across the recursion).
Also, I find it's helpful, when doing these sorts of algorithms, to think as functionally as possible (in the paradigm sense - i.e., avoid side effects and mutability, and proceed by creating new objects). You had a workable approach using list to make copies (although it is clearer to use the .copy method), but I think there's a cleaner way, as shown below.
All this advice leads us to:
def place_nth_element(elements, nth, start_at, output_so_far):
last_pos = len(output_so_far) - len(elements) + nth
for pos in range(start_at, last_pos+1):
output = output_so_far[:pos] + (elements[nth],) + output_so_far[pos+1:]
if nth == len(elements)-1:
yield output
else:
yield from place_nth_element(elements, nth+1, pos+1, output)
def allposs(elements, output_length):
return list(place_nth_element(elements, 0, 0, (0,)*output_length))
HOWEVER, I would not solve the problem that way - because the standard library already offers a neat solution: we can find the itertools.combinations of indices where a value should go, and then insert them. Now that we no longer have to think recursively, we can go ahead and mutate values :)
from itertools import combinations
def place_values(positions, values, size):
result = [0] * size
for position, value in zip(positions, values):
result[position] = value
return tuple(result)
def possibilities(values, size):
return [
place_values(positions, values, size)
for positions in combinations(range(size), len(values))
]
Hi i have a nested list which i should sum it from recursive function
how can i do that?
my solution didn't work
def n_l_sum(n):
s=0
for i in n:
if i==list:
s+=i
else:
s+=n_l_s(n)
return s
use the isinstanceof function instead of using the ==
def summer(lst):
if not isinstance(lst, list) : return lst
sum = 0
for x in lst:
sum += summer(x)
return sum
def main():
a= [1,2,3, [5,6]]
print(summer(a))
Recursion can be quite difficult to grasp at first; because it can be very intuitive to conceptualize but an pain to implement. I will try to explain my answer as thoroughly as possible so that you understand what exactly is happening:
def n_l_sum(n,i):
s=0 #somewhere to store our sum
lenLst= len(n)
cur = n[i] #sets current element to 'cur'
if lenLst-1-i < 1: #this is the base case
return cur
else:
s = n_l_sum(n,i+1)+cur #where the actual iteration happens
print(s) #give result
return s
n=[6,4]
n_l_sum(n,0)
The Base Case
The first important thing to understand is the base case this gives the recursion a way of stopping.
For example without it the function would return an IndexError because eventually n_l_sum(n,i+1) would eventually go over the maximum index. Therefore lenLst-1-i < 1 stops this; what it is saying: if there is only one element left do this.
Understanding Recursion
Next, I like to think of recursion as a mining drill that goes deep into the ground and collects its bounty back on the way up to the surface. This brings us to stack frames you can think of these as depths into the ground (0-100 m, 100-200 m). In terms of programming they are literal frames which store different instances of variables. In this example cur will be 4 in frame 1 then 6 in frame 2.
once we hit the base case we go back up through the frames this is where s = n_l_sum(n,i+1)+cur does the legwork and calculates the sum for us.
If you are still struggling to understand recursion try and run this code through http://www.pythontutor.com/visualize.html#mode=edit to see what is happening on a variable level.
def n_l_s(n):
s=0
for i in n:
if type(i) != type([]):
s+=i
else:
s+=n_l_s(i)
return s
n=[3, -1, [2, -2], [6, -3, [4, -4]]]
print(n_l_s(n))
output:
5
The functional way would be to to this:
def list_sum(l):
if not isinstance(l, list): # recursion stop, l is a number
return l
# do list_sum over each element of l, sum result
return sum(map(list_sum, l))
Here's my python code
def search(myList, number):
for i in myList:
if i[0] == number:
return i[1]
return None
myList = [(5107261, 'Ernst'), (6524256, 'Arvo')]
number = 5107261
print(search(myList, number))
Now I want to write it using recursion but I'm not sure how to do it. I need some pointers to help me get started.
When writing recursive code, you want to define a base case, and you want to define a method for making your problem smaller on every step. In this example, we are working with lists, so a good base case would be an empty list, []. If the list is empty, it makes sense to return None. In your recursive case, you want to do some work to make the problem smaller. In this case we can check one element, and if that element is not what we are searching for, we can call the function again on a smaller version of the list.
Our result is a function like this:
def searchR(myList, number):
if length(myList) == 0: return None
elif myList[0][0] == number: return myList[0][1]
else: return searchR(myList[1:], number)
There are 3 cases. Case 1 is our base case, where the length of the list is 0. Case 2 is our success case, where we found the the target of the search. Case 3 is where we make our recursive call. Notice how the first element is removed from the new list! If the first element isn't removed, the function will loop forever.
I am trying to take,as arguments, two values: a list and a target value. It returns the # number of times that the target value appears in the list.
def countTarget(myList, target):
#initialize the counter to zero
counter = 0
for element in myList:
#compare the value in the list to the target value
#if they are the same, increment the counter
if element == target:
counter = counter + 1
return counter
Okay there are going to be better answers, but here's one.
def countTarget(myList, target):
return sum([x == target for x in myList])
Edit
There's a much better alternative in the comments.
myList.count(target)
...
Use the Counter class. It's a special type of dict that works very well for counting the frequency of a particular value inside a list (as opposed to using a dict yourself) because it will circumvent some nits like running into a KeyError when trying to count a target that doesn't exist in the list (Counter on the other hand will just return 0).
from collections import Counter
def countTarget(myList, target):
return Counter(myList)[target]
Alternatively, you can use the bult in count function for lists, as mentioned in a comment below your question.
def countTarget(myList, target): return myList.count(target)
However, at that point your countTarget function isn't doing you much good. If you really want to have both objects be parameters, you can also use the count function from a static context...
list.count(myList, target)
But seriously, just use myList.count(target). It's probably the most simple and straightforward for your use case (by the looks of it). If you need to count targets multiple times, then consider keeping your own Counter as mentioned before.