I am working on a code golf and attempting to write a recursive function using the tuple boolean evaluation syntax ((a,b)[bool]). My function is as follows (with some helpful prints):
def b(A,k,n):
m=len(A)/2
print "A: ", A
print "m: ", m
print "n: ", n
print "A[m]", A[m]
if A[m]==k:return m+n
# return (m+n,(b(A[m:],k,m+n),b(A[:m],k,n))[A[m]<k])[A[m]!=k]
return m+n if A[m]==k else (b(A[:m],k,n) if A[m]>k else b(A[m:],k,m+n))
Arguments A, k, and n are the input list to search, the key, and the index in the original A which a sublist starts at. m is the middle entry of the list. If I search [1,2,3,4,5,6,7] for 6, I would expect to see the following output
A: [1, 2, 3, 4, 5, 6, 7]
m: 3
n: 0
A[m] 4
A: [4, 5, 6, 7]
m: 2
n: 3
A[m] 6
The correct output and result are produced with the non-commented return statement. However, when I attempt to use the commented return statement (which should be equivalent), I receive a recursion depth error.
Moreover, if I replace one of the recursive calls with a determinate value, then search for a value on the edge, it functions as expected. For example, searching for 6 in [1,2,3,4,5,6,7] works correctly if my return statement is:
return (m+n,(b(A[m:],k,m+n),"anyValue")[A[m]>k])[A[m]!=k]
"anyValue" is never returned by my conditional in this case, so why does it matter what value I put in there?
Why do these two statements evaluate differently?
When you do
thing() if condition else other_thing()
only one of thing() and other_thing() is evaluated. The one that isn't returned doesn't get evaluated at all, so you can do things like
base_case_value if base_case_condition else continue_recursing()
and the recursion will stop at the base case.
When you do
(continue_recursing(), base_case_value)[base_case_condition]
Python needs to build the tuple before it can be indexed, so both base_case_value and continue_recursing() are evaluated no matter what. Your function doesn't stop when it hits the base case.
The problem is that the two statements aren't equivalent. In the working "if" case, you only call b once per round. In the commented out "tuple boolean" case, you evaluate both b conditions before selecting which one to use with the boolean selector. Since b is recursive, and you always run the "bad" non terminating side along with the "good" terminating side, you get max recursion error.
Related
I'm still new to generators in python. I was trying out one on my own and tried to something really simple:
def fib(a):
... if a==0 or a==1:return 1
... yield fib(a-1)+fib(a-2)
print(list(fib(5))
This code gave me this error:
TypeError: unsupported operand type(s) for +: 'generator' and 'generator'
Can't generators be used in this manner?
Calling a generator function doesn't produce the next value. It produces a generator object, a specialist version of an iterator object. You have, in effect, something that wraps a paused function.
To get another value from that function, you'd have to call next(iterator) on the object or use something like list(iteratort) or for ... in iterator to loop over the object and get the values out. See What does the "yield" keyword do? for more details.
So here, the you'd have to use next(fib(a-1)) + next(fib(a-2)) to get the two recursive values out. That'll also fail, because your termination case (a == 0 or a == 1) uses return (translated into the value of a StopIteration exception) and not yield; you'd have to fix that too.
And this highlights why your recursive function should not be a generator function. Your function doesn't produce a series of values to iterate over. There's just one result mfor a given argument value. You'd be far better off to just use return and not yield.
If you wanted to generate a sequence of fibonacci numbers, the function argument would need to be seen as a limit; "give me the first n fibonacci numbers". The following iterative function does that:
def first_n_fibonacci(n):
a, b = 0, 1
for i in range(0, n):
a, b = b, a + b
yield a
This would give you a list of the first n fibonacci numbers, as a generator:
>>> f = first_n_fibonacci(5)
>>> f
<generator object first_n_fibonacci at 0x10b2c8678>
>>> next(f)
1
>>> next(f)
1
>>> list(f)
[2, 3, 5]
or you could use the argument to produce all fibonacci values up to a limit, or to produce an endless generator of fibonacci numbers. None of those would require recursion, a loop like the above suffices.
Generators are meant to be used for iterations, and yet by adding two of the returning values from fib you are trying to use it as a scalar value, which is confirmed by your terminal condition (where a equals to 0 or 1) also returning a scalar value.
You should simply use return in this case.
def fib(a):
if a==0 or a==1:return 1
return fib(a-1)+fib(a-2)
print(fib(5))
If you do want to use a generator, you need to think about outputting a sequence, and not a single value. Do you want your fib(a) to output the ath fib number or the the 1st, 2nd, 3rd, 4th, 5th ... ath fib number? If the latter, then generators are good for this.
Here is an example generator for the Fibonacci numbers from the 1st to the nth number.
def fib(n):
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b
I'm using Python 3.4.* and I am trying to execute the following code:
def P(n):
if n == 0:
yield []
return
for p in P(n-1):
p.append(1)
yield p
p.pop()
if p and (len(p) < 2 or p[-2] > p[-1]):
p[-1] += 1
yield p
print(P(5)) # this line doesn't make sense
for i in P(5): # but this line does make sense thanks to furkle
print(i)
but I am getting <generator object P at 0x02DAC198> rather than the output.
Can someone explain where in my code needs to be fixed? I don't think py likes the function name P but I could be wrong.
Edit: furkle clarified <generator object P at 0x02DAC198>.
By the way, I'm currently trying to write my own modified partition function and I was trying to understand this one corresponding to the classical setting.
I think you're misunderstanding the concept of a generator. A generator object is like a list, but you can iterate through its results lazily, without having to wait for the whole list to be constructed. Calling an operation on a function that returns a generator will not perform that operation in sequence on every item yielded by the generator.
If you wanted to print all the output of P(5), you should write:
for i in P(5):
print(i)
If you just want to print a list of the content returned by the generator, that largely seems to defeat the purpose of the generator.
Many things are wrong with this code, and your understanding of how generators work and what they are used for.
First, with respect to your print statement, that is exactly what it should print. Generators are never implicitly expanded, because there is no guarantee that a generator would ever terminate. It's perfectly valid, and sometimes very desirable, to construct a generator that produces an endless sequence. To get what you'd want (which I assume is produce output similar to a list), you'd do:
print(list(P(5))
But that brings me to my second point; Generators yield values in a sequence (in 99% of uses for them, unless you're using it as a coroutine). You are trying to use your generator to construct a list; however, if n is not 0 this will never yield a value and will immediately return. If you goal is to construct a generator that makes a list of 1's of a given length, it should look like this:
def P(n):
while n >= 0:
yield n
n -=1
This will produce a sequence of 1's of length n. To get the list form, you'd do list(P(n)).
I suggest you have another read over the Generator Documentation and get a better feel for them and see if they're really the right tool for the job.
In reading the function, I try to find what the call will produce. Let's start with the full original code:
def P(n):
if n == 0:
yield []
return
for p in P(n-1):
p.append(1)
yield p
p.pop()
if p and (len(p) < 2 or p[-2] > p[-1]):
p[-1] += 1
yield p
print(P(5)) # this line doesn't make sense
Okay, so it calls P(5). Since that's not 0, P recurses, until we reach P(0) which yields an empty list. That's the first time p receives a value. Then P(1) appends 1 into that list, and yields it to P(2) which repeats the process.. and so on. All the same list, originally created by P(0), and eventually yielded out as [1,1,1,1,1] by P(5) - but then the magic happens. Let's call this first list l0.
When you ask the generator for the second item, control returns to P(5) which now removes a value from l0. Depending on a bunch of conditions, it may increment the last value and yield p, which is l0, again. So the first item we received has been changing while we asked for the second. This will eventually terminate, but means there's a difference between these two:
print list(P(5)) # Eventually prints a list of l0 which has been emptied!
for item in P(5):
print item # Prints l0 at each point it was yielded
In [225]: for i in P(5): print i
[1, 1, 1, 1, 1]
[2, 1, 1, 1]
[2, 2, 1]
[3, 1, 1]
[3, 2]
[4, 1]
[5]
In [226]: list(P(5))
Out[226]: [[], [], [], [], [], [], []]
This is why I called it post-modifying; the values it returns keep changing after they've been produced (since they are in fact the same object being manipulated).
No this isn't homework but it is on our study guide for a test. I need to understand the role the return statement plays and the role recursion plays. I don't understand why the function doesn't break after x = 1.
def thisFunc(x):
print(x)
if x>1:
result=thisFunc(x-1)
print(result)
return x+1
Sorry, I understand how elementary this is but I could really use some help. Probably why I can't find an explanation anywhere...because it's so simple.
edit: Why does it print out what it does and what and why is the value of x at the end? sorry if I'm asking a lot I'm just frustrated
When you enter the function with a value n>1 it prints the current value, and then calls it's self with n-1. When the inner function returns it returns the value n - 1 + 1 which is just n. Hence, the function prints out the value n twice, once before the inner recursion and once after.
If n == 1, which is the base case, the function only prints 1 once and does not call it self again (and hence does not get result back to print). Instead it just returns, hence why 1 is only printed once.
Think of it like an onion.
calling thisFunc(n) will result in
n
# what ever the output (via print) of thisFunc(n-1) is
n
I don't understand why the function doesn't break after x = 1.
But it does:
>>> ================================ RESTART ================================
>>> x = 1
>>> def thisFunc(x):
print("Function called on x-value: ", x)
if x > 1:
result = thisFunc(x-1)
print(result)
return x+1
>>> thisFunc(x)
Function called on x-value: 1
2
>>>
edit: Why does it print out what it does and what and why is the value of x at the end?
Well, it prints it out because you're telling it to. Try following the value of x as you go through the function ("x is one, one is not bigger than 1; return 1+1. Ok. [new case] x is two, two is bigger than 1..." and so on).
return and recursion are part and parcel of programming; return statements designates the end of a function (even if you might have several lines more of code) and they also pass data back to whatever asked them for it. In your case you're asking "what happens when x is 1, given these rules?"; the returned data is your answer.
Recursion is simply the matter of letting the function call itself, should it (you) need to. You simply tell the program that "hey, as long as x is bigger than 1, call this function [that just so happens to be the same function initially called] on it and let it do its thing". To get a better understanding of your function I'd suggest that you add the line "Function called on x-value: " to the first print statement inside the function, or at least something that lets you identify which printed line is x and which is result.
For a more in-depth explanation on recursion, I recommend Recursion explained with the flood fill algorithm and zombies and cats
#Calculates to the index position of a fib number.
def f3(n):
if n < 2:
return n
return f3(n-2) + f3(n-1)
The function only accepts one argument, yet two are being sent in the return, yet, it works! What's happening here?
If I return f3(n-3), the function breaks down. What effect does the concatenation have?
Addition results in a single value.
>>> 1 + 2
3
>>> [1] + [2]
[1, 2]
Python evaluates the expression f3(n-2) + f3(n-1) before returning it, so its actually returning the value of them combined. The same is the case for f3(n-2), its first evaluating n-2 and then passing it as a value to f3().
The number of return arguments has nothing to do with the number of arguments a function takes as input.
The line f3(n-2) + f3(n-1) is returning only one value, the result of calculating f3 for the input n-2 and then adding that value to the result of calculating f3 for the input n-1
In Python, the mechanism for returning multiple values from a function is by packing them inside a tuple and then extracting them at the time of invoking the function (not the case in your question!) For example:
def multivalue(x, y)
return (x, y)
a, b = multivalue(5,10)
# here a holds 5, and b holds 10
print max(3 for i in range(4))
#output is 3
Using Python 2.6
The 3 is throwing me off, heres my attempt at explaining whats going on.
for i in range(4) makes a loop that loops 4 times, incrementing i from 0 to 3 at the start of each loop. [no idea what the 3 means in this context...] max() returns the biggest iterable passed to it and the result is printed to screen.
3 for i in range(4) is a generator that yields 3 four times in a row and max takes an iterable and returns the element with the highest value, which is, obviously, three here.
This evaluates to:
print max([3,3,3,3])
... which is an elaborate way to say print 3.
expr for x in xs is a generator expression. Typically, you would use x in expr. For example:
[2*i for i in range(4)] #=> [0, 2, 4, 6]
It can be rewritten as:
nums = []
for i in range(4):
nums.append(3)
print max(nums) # 3! Hurrah!
I hope that makes its pointlessness more obvious.
The expression:
print max(3 for i in range(4))
is printing the result of the max() function, applied to what is inside the parentheses. Inside the parentheses however, you have a generator expression creating something similar to an array, with all elements equal to 3, but in a more efficient way than the expression:
print max([3 for i in range(4)])
which will create an array of 3s and destroy it after it is no longer needed.
Basically: because inside the parentheses you will create only values that are equal, and the max() function returns the biggest one, you do not need to create more than one element. Because with the number of elements always equal to one, the max() function becomes not needed and your code can be effectively replaced (at least in the case you have given) by the following code:
print 3
That is simply all ;)
To read more about differences between comprehension and generator expression, you can visit this documentation page.