Search code examples
pythongenerator

Yield with recursion in python


I am trying to generate a Set product of two string using generator approach than storing it in a list as follows:

def recursive_generator(l_set, index = 0, value = ""):
    if index == len(l_set):
        yield(value)
    else:
        for i in l_set[index]:
            recursive_generator(l_set, index + 1, value + i)

def main():
    _input = ["ab","de"]
    for i in recursive_generator(_input):
        print(i)

main()

The expected outcome is ad ae bd be new line seperated but on execution, there is no result. On checking with pdb to see the behaviour:

$ python3 -m pdb recurse.py 
> /home/ec2-user/recurse.py(1)<module>()
-> def recursive_generator(l_set, index = 0, value = ""):
(Pdb) n
> /home/ec2-user/recurse.py(8)<module>()
-> def main():
(Pdb) n
> /home/ec2-user/recurse.py(13)<module>()
-> main()
(Pdb) step
--Call--
> /home/ec2-user/recurse.py(8)main()
-> def main():
(Pdb) n
> /home/ec2-user/recurse.py(9)main()
-> _input = ["ab","de"]
(Pdb) n
> /home/ec2-user/recurse.py(10)main()
-> for i in recursive_generator(_input):
(Pdb) step
--Call--
> /home/ec2-user/recurse.py(1)recursive_generator()
-> def recursive_generator(l_set, index = 0, value = ""):
(Pdb) locals()
{'l_set': ['ab', 'de'], 'index': 0, 'value': ''}
(Pdb) n
> /home/ec2-user/recurse.py(2)recursive_generator()
-> if index == len(l_set):
(Pdb) len(l_set)
2
(Pdb) n
> /home/ec2-user/recurse.py(5)recursive_generator()
-> for i in l_set[index]:
(Pdb) n
> /home/ec2-user/recurse.py(6)recursive_generator()
-> recursive_generator(l_set, index + 1, value + i)
(Pdb) step
--Call--
> /home/ec2-user/recurse.py(1)recursive_generator()
-> def recursive_generator(l_set, index = 0, value = ""):
(Pdb) locals()
{'l_set': ['ab', 'de'], 'index': 1, 'value': 'a'}
(Pdb) n
GeneratorExit   <<<<< It did not proceed for the next loop, did not even check the if condition


> /home/ec2-user/recurse.py(1)recursive_generator()
-> def recursive_generator(l_set, index = 0, value = ""):
(Pdb) w
  /usr/lib64/python3.7/bdb.py(578)run()
-> exec(cmd, globals, locals)
  <string>(1)<module>()
  /home/ec2-user/recurse.py(13)<module>()
-> main()
  /home/ec2-user/recurse.py(10)main()
-> for i in recursive_generator(_input):
  /home/ec2-user/recurse.py(6)recursive_generator()
-> recursive_generator(l_set, index + 1, value + i)
> /home/ec2-user/recurse.py(1)recursive_generator()
-> def recursive_generator(l_set, index = 0, value = ""):
(Pdb) n
Internal StopIteration
> /home/ec2-user/recurse.py(10)main()
-> for i in recursive_generator(_input):
(Pdb) n
--Return--
> /home/ec2-user/recurse.py(10)main()->None
-> for i in recursive_generator(_input):
(Pdb) w
  /usr/lib64/python3.7/bdb.py(578)run()
-> exec(cmd, globals, locals)
  <string>(1)<module>()
  /home/ec2-user/recurse.py(13)<module>()
-> main()
> /home/ec2-user/recurse.py(10)main()->None
-> for i in recursive_generator(_input):
(Pdb) n
--Return--
> /home/ec2-user/recurse.py(13)<module>()->None
-> main()
(Pdb) w
  /usr/lib64/python3.7/bdb.py(578)run()
-> exec(cmd, globals, locals)
  <string>(1)<module>()->None
> /home/ec2-user/recurse.py(13)<module>()->None
-> main()

I am not able to understand the flow break here on why it will perform a generator exit and stops the iteration. Is this approach supported ? I am a noob when comes to python and am learning generators and iterators. Any guidance is appreciated.


Solution

  • What you are doing is just calling recursive_generator in the loop but not yielding any values (similar to returning a value in a non-generator recursive function where you just return the recursive function call). You need to use yield from inside the loop to get the recursive behavior of the generator function.

    def recursive_generator(l_set, index = 0, value = ""):
        if index == len(l_set):
            yield(value)
        else:
            for i in l_set[index]:
                yield from recursive_generator(l_set, index + 1, value + i)         
    

    OUTPUT:

    ad
    ae
    bd
    be