In short. How do I write something else than this: for another in combinationOfK(K-1, L[i+1:]): My function combinationOfK(...) is not iterable.
I am trying to understand the code from here, solution to. Problem 26: Generate the combinations of K distinct objects chosen from the N elements of a list
I know what yield does. But I am trying to write the code without a yield statement. Code with yield statement is this.
def combination(K, L):
if K<=0:
yield []
return
for i in range(len(L)):
thisone = L[i:i+1]
for another in combination(K-1, L[i+1:]):
yield thisone + another
The question, yield-keyword-explained gave me the idea that I could replace yield. The recepie they give, which is not working for me, is:
When you see a function with
yieldstatements, apply this easy trick to understand what will happen:
- Insert a line
result = []at the start of the function.- Replace each
yield exprwithresult.append(expr).- Insert a line
return resultat the bottom of the function.- Yay - no more
yieldstatements! Read and figure out code.- Revert function to original definition.
Using this to get code without yield give me this. The code is not working (the function is not iterable). What do I have to write to get this code working without yield?
def combinationOfK(K,L):
result = []
if K <= 0:
result.append([])
return
for i in range(len(L)):
thisone = L[i:i+1]
for another in combinationOfK(K-1, L[i+1:]): # the error
result.append(thisone + another)
return result
I am using this code to test the function,
the_list = ['a','b','c','d','e']
print list(combinationOfK(2, the_list))
raising error TypeError: 'NoneType' object is not iterable.
The problem is that your original code uses return in an unusual way.
def combination(K, L):
if K<=0:
yield []
return # <--- hmmm
Most of the time you won't see return in a generator, because you don't often need it. Usually, generators simply "fall off" at the end; the interpreter reaches the end of the generator without encountering a return statement, and then it knows to throw StopIteration.
In this case, the writer of the code has inserted a return statement to "hurry up" the process. When K <= 0, there's no more work to be done, so the generator can throw StopIteration -- but without the return statement, it would go into the for loop, producing incorrect results. In my opinion, a clearer way to do this would have been like so:
def combination(K, L):
if K<=0:
yield []
else:
for i in range(len(L)):
thisone = L[i:i+1]
for another in combination(K-1, L[i+1:]):
yield thisone + another
Now the conversion works as expected:
def combination2(K, L):
result = []
if K <= 0:
result.append([])
else:
for i in range(len(L)):
thisone = L[i:i + 1]
for another in combination2(K - 1, L[i + 1:]):
result.append(thisone + another)
return result
As Vincent mentioned, your function is returning None because of the 5th line. Change it to this:
def combinationOfK(K,L):
result = []
if K <= 0:
result.append([])
return result
for i in range(len(L)):
thisone = L[i:i+1]
for another in combinationOfK(K-1, L[i+1:]): # the error
result.append(thisone + another)
return result
However, why are you against yield? Generators make for readable, efficient code. The point of the Yield Keyword Explained article was not to dispense with it, but rather, to explain it.
In the generator code you posted:
def combination(K, L):
if K<=0:
yield []
return
for i in range(len(L)):
thisone = L[i:i+1]
for another in combination(K-1, L[i+1:]):
yield thisone + another
The return statement does NOT mean the same thing as return does in a normal function. In a generator, return immediately raises StopIteration, which causes the caller to stop iterating over the generator object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With