Python – Replace functions with something iterable

Replace functions with something iterable… here is a solution to the problem.

Replace functions with something iterable

In a nutshell. How do I write something else: for another in combinationOfK(K-1, L[i+1:]): My function combinationOfK(…) is not iterative.

I’m trying to understand code in here, solution. Problem 26: Generate a combination of K different objects selected from N elements of the list
I know what yield does. But I’m trying to write code without the yield statement. The code with yield statement is like 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 made me think I could replace yield. The receipt they provided didn’t work for me, yes:

When you see a function with yield statements, apply this easy
trick to understand what will happen:

  1. Insert a line result = [] at the start of the function.
  2. Replace each yield expr with result.append(expr).
  3. Insert a line return result at the bottom of the function.
  4. Yay – no more yield statements! Read and figure out code.
  5. Revert function to original definition.

Use it to get code without generating yield to give me this. Code does not work (functions are not iterative). What do I have to write to make this code run 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’m using this code to test functionality

the_list = ['a','b','c','d','e']
print list(combinationOfK(2, the_list))

Throws an error TypeError: 'NoneType' object is not iterable.

Solution

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 the generator because you don’t need it very often. Usually, the generator just “drops” at the end; The interpreter reaches the end of the generator without encountering the return statement, and then it knows to throw a StopIteration .

In this case, the code writer inserts a return declaration to “speed up” the process. When K <= 0, there is no more work to do, so the generator can throw a StopIteration — but without the return declaration, it will enter a for loop, producing incorrect results. In my opinion, a clearer approach would be something like this:

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

Related Problems and Solutions