I recently studied a python recursion function and found that the recursion stops when it uses element in []. So I made a simple test function, found that there is even no print out. So how can I understand the element in []? Why does the function stop when referring to element in []?
b=1
def simple():
for i in []:
print('i am here')
return i+b
a = simple()
Python's in keyword has two purposes.
One use in as part of a for loop, which is written for element in iterable. This assigns each value from iterable to element on each pass through the loop body. This is how your example function is using in (though since the list you're looping over is empty, the loop never does anything).
The other way you can use in is as an operator. An expression like x in y tests if element x is present in container y. (There's also a negated version of the in operator, not in. The expression x not in y is exactly equivalent to not (x in y).) I suspect this is what your recursive code is doing. This would also not be useful to do with an empty list literal (since an empty list by definition doesn't contain anything), but I'm guessing the real recursive function is a bit more complicated.
As an example of both uses of in, here's a generator function that uses a set to filter out duplicate items from some other iterable. It has a for loop that has in, and it also uses in (well, technically not in) as an operator to test if the next value from the input iterator is contained in the seen set:
def unique(iterable):
seen = set()
for item in iterable: # "in" used by for loop
if item not in seen: # "in" used here as an operator
yield item
seen.add(item)
A recursive function calls itself n-number of times, then returns a terminating value on the last recursion that backs out of the recursive stacks.
Example:
compute the factorial of a number:
def fact(n):
# ex: 5 * 4 * 3 * 2 * 1
# n == 0 is your terminating recursion
if n == 0:
return 1
# else is your recursion call to fact(n-1)
else:
return n * fact(n-1)
In your example, there is no recursive call to simple() within the function, nor are there any element inside the empty list [] to step through, therefore your for loop never executed
Its concerned about mechanism of 'for loop'.
Superficially, the iterator you want to travese (which is "[]" in you example) has a length of 0, so the body of the loop (which include "print" an so on) will not be executed.
Hope it helps.
Related
This question already has answers here:
Return and yield in the same function
(4 answers)
Closed 4 months ago.
I tried to create a simple Python function where the result is generator or normal output depending on a condition.
But the function did not work. it seems the presence of the keyword 'yield' in the function leads the function to return a generator in all cases.
Below is a simple function that demonstrate what I tried to do.
# Generator function
def square_gen(numbers):
if len(numbers) > 3:
for number in numbers:
yield number**2
else:
output= []
for number in numbers:
output.append(number**2)
return output
The plan is to call the function in two different ways and get different corresponding outputs:
square_gen([1,2,3]) returns a generator object that I can iterate over (since length of the argument is not greater than 3) .
square_gen([1,2,3,4]) returns [1,4,9,16] (since length of the argument is greater than 3).
However, only 1) works. It returns a generator object that one can iterate over. Surprisingly, 2) still returns a generator object (I wonder why) but looping over this generator object results in an error.
So basically, I want to know why the non-generator branch of the function still returns a generator object.
You are right in your assumption, having 'yield' anywhere in your method makes a generator, even if it's under an 'if'.
Not that there are many reasons to do it, but if you want to achieve what you asked you can do it like this
def square_gen(numbers):
if len(numbers) > 3:
def gen():
for number in numbers:
yield number**2
return gen()
else:
output= []
for number in numbers:
output.append(number**2)
return output
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)
I am trying to write a code that returns every prime palindrome with three digits. Here is my code:
def digpalprim():
for x in range (100,1000):
if prime(x)=='prime':
if str(x)==str(x)[::1]:
return x
I've already defined the prime(x) function, it works well, that stage just determines whether x is prime or not. All in all the code works, except that it only gives me the first such a palindrome. I don't really understand why, shouldn't the program consider all the numbers between 100 and 1000? Please help?
Your function returns as soon as it finds the first such palindrome; return exits a function.
Collect your finds in a list and return that:
def digpalprim():
palindromes = []
for x in range (100,1000):
if prime(x)=='prime':
if str(x)==str(x)[::1]:
palindromes.append(x)
return palindromes
or you can make your function a generator by replacing the return with a yield statement:
def digpalprim():
for x in range (100,1000):
if prime(x)=='prime':
if str(x)==str(x)[::1]:
yield x
Now you'll have to iterate over this function or use list() to 'pull' all values out:
all_palindromes(digpalprim())
or
for palindrome in digpalprim():
print(palindrome)
You are returning the function the first time you encounter one.
def digpalprim():
palprimes = []
for x in range (100,1000):
if prime(x)=='prime':
if str(x)==str(x)[::1]:
palprimes.append(x)
return palprimes
This creates a list at the start of the function and appends each valid palindrome prime to that list, then returns the entire list (after completing all loops) instead of returning just the first one encountered.
Just remember, if Python hits a return statement, it's going to stop function execution right there and return that value regardless of any additional loops or code you may intend to be executed.
The function returns and ends as soon as the first result is found.
You may wish to add the results to a list and then print out the list.
return x This statement causes the program to return to the calling function once this statement is encountered. To return all you may put it in an list. For e.g:
you can have a list called values and append it to it, and finally return it at the end
For such small tasks, I prefer using list comprehensions:
palindromes = [x for x in range(100, 1000) if (prime(x) == 'prime') and (str(x) == str(x)[::1])]
Or (equivalently):
condition = lambda f: prime(f) == 'prime' and str(f) == str(f)[::1]
palindromes = [x for x in range(100, 1000) if condition(x)]
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.
I'm trying to halt the for loop below once values (x,y) or (z,2) have been returned so that the value i doesn't keep increasing, and simply halts when the if or elif condition is first
def maxPalindrome(theList):
# students need to put some logic here
maxcomplist = theList[:]
maxcomplist.reverse()
control = len(theList) - 1
# exit if maxPalindrome is True
for i in range(control):
if maxcomplist[:] == theList[:]:
x = 0
y = len(theList)
return (x, y)
break
elif maxcomplist[i:control] == theList[i:control]:
successList = theList[i:control]
z = i
w = len(theList) - z - 1
return (z, w)
How can I accomplish this?
As I wrote in a comment already: that function isn't a recursive one at all.
Recursion means, that a function calls itself to complete it's purpose. This call can be indirect, meaning that the function uses helper function that will call the first function again.
But your code doesn't cover both cases.
A recursive function always have a certain architecture:
the first thing after being called should be to test, if the primitive case (or one primitive case among options) has been reached. if so, it returns.
If not it will compute whatever is needed and pass this results to itself again,
untill the primitive case is reached, and the nested function calls will finish in one after the other.
One well-known usage of recursion is the quicksort algorithm:
def quicksort(alist):
if len(alist) < 2:
return alist # primitive case: a list of size one is ordered
pivotelement = alist.pop()
# compute the 2 lists for the next recursive call
left = [element for element in alist if element < pivotelement]#left = smaller than pivotelemet
right = [element for element in alist if element >= pivotelement]#left = greater than pivotelemet
# call function recursively
return quicksort(left) + [pivotelement] + quicksort(right)
So the "stop" must be the return of a primitive case. This is vital for recursion. You cannot just break out somehow.
I don't understand the question - if I get it right, that what you want already happens. If you return, the function stops running.
Some comments in addition to this answer:
As well, I cannot see where the function is called recursively, nor what
exit if maxPalindrome is True
means. (Is this a comment, maybe?)
Besides, the maxcomplist[:]==theList[:] does not make much sense to me, and seem to be a waste of time and memory, and to have this comparison in each iteration loop doesn't make it faster as well.