1

I am missing something about how recursion work in Python. I have put in place the following method in place to tokenize a sentence:

def extractIngredientInfo(ingredientLine, sectionTitle):

    print 'extractIngredientInfo' + ingredientLine

    # set-up some default values for variables that will contains the extracted datas
    usmeas             = 'N.A'
    othermeas          = 'N.A'

    p_ingredientalt     = re.compile('\(or\s(.*?)\)')
    malt = p_ingredientalt.search(ingredientLine)
    if malt:
        ingredientAlt = malt.group(1)
        ingredientLine = ingredientLine.replace(malt.group(0), '').strip()
        print 'NEW LINE TO TREAT(ALT)' + ingredientLine
        extractIngredientInfo(ingredientLine, sectionTitle)
        usmeas,othermeas = extractOneIngredientInfo(ingredientAlt)
        print 'MALT'
        ingredient 
        yield usmeas, othermeas
        #return;

    p_ingredientpurpose = re.compile('\(for\s(.*?)\)') 
    mpurpose = p_ingredientpurpose.search(ingredientLine)
    if mpurpose:
        ingredientPurpose = mpurpose.group(1)
        ingredientLine = ingredientLine.replace(mpurpose.group(0), '').strip()
        print 'NEW LINE TO TREAT(FOR)' + ingredientLine
        extractIngredientInfo(ingredientLine, sectionTitle)
        usmeas,othermeas = extractOneIngredientInfo(ingredientPurpose)
        print 'MPURPOSE'
        yield usmeas,othermeas
        #return;

    usmeas,othermeas = extractOneIngredientInfo(ingredientLine)
    print 'FINAL'
    yield usmeas, othermeas

when i am making a calling to this function, I have a match for malt which should lead to an immediate call to the recursive function extractIngredientInfo but this never happening (I don't see the second call to print 'extractIngredientInfo' + ingredientLine. Is there any specific reason this is not happening?

tiguero
  • 11,477
  • 5
  • 43
  • 61
  • You do see the `'NEW LINE TO TREAT'` line being printed? – Martijn Pieters Aug 31 '12 at 14:25
  • Also, this function is a generator, but you never use the results produced by the inner calls to `extractIngredientInfo`. – Martijn Pieters Aug 31 '12 at 14:26
  • @MartijnPieters yes i see it. – tiguero Aug 31 '12 at 14:27
  • I think you are confusing the recursion and generator functions (which this is an example of), have a look at the output of `for line in extractIngredientInfo(): print line` – Andy Hayden Aug 31 '12 at 14:28
  • @hayden you are probably right - I can't combine those ? – tiguero Aug 31 '12 at 14:29
  • 1
    You can, but you need to actually iterate over generator functions for them to progress. – Martijn Pieters Aug 31 '12 at 14:30
  • 1
    You should read up on generators and how they work. One fundamental thing is that you don't just "call" a generator function. You iterate over it, either in a `for` loop, or explicitly with `next()`. [Here is a fairly nice article on generators.](http://linuxgazette.net/100/pramode.html) If you are new to Python, and especially if you are new to programming, you probably just want a regular recursive function, rather than a recursive generator. – John Y Aug 31 '12 at 14:41
  • @tiguero you can definitely combine (see my answer), you just need to take care what your function actually yields - it's easy to be tripped up! – Andy Hayden Aug 31 '12 at 14:46
  • possible duplicate of [Python: using a recursive algorithm as a generator](http://stackoverflow.com/questions/248830/python-using-a-recursive-algorithm-as-a-generator) – tchrist Sep 01 '12 at 22:36

3 Answers3

1

I assume this has to do with the fact that you're not actually using the output of your function recursively. It's hard to say what you want to do with it, but you probably want to do something with it. e.g.:

 for res in  extractIngredientInfo(...,...):
     yield res

instead of just:

extractIngredientInfo(...,...)
mgilson
  • 300,191
  • 65
  • 633
  • 696
1

Your function returns a generator because it uses yield statements. A generator is paused until you request the next value.

This means that a generator function doesn't do anything until you call .next() it, or use it as an iterator in a loop:

>>> def foo():
...     print 'Foo called'
...     yield 'bar'
...     print 'Still in foo'
...     yield 'baz'
... 
>>> foogen = foo()
>>> foogen.next()
Foo called
'bar'
>>> foogen.next()
Still in foo
'baz'
>>> for val in foo():
...     pass
... 
Foo called
Still in foo

Notice how the message Foo called is not printed until I call .next() on the generator.

You only call your recursive function, but that returns a generator that you then discard. The code itself is never executed because it is kept on hold. Loop over the results instead:

for res in extractIngredientInfo(ingredientLine, sectionTitle):
    yield res

Now you actually iterate over the nested generator function results, and pass them on to the caller (the consumer of the outer nested generator function).

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
1

You have to take care how you use recursion in a generator function. You have to be careful what your generator function yields.

def gen_f(n):
    for i in xrange(n):
        yield "hello"

def recursive_f(n):
    yield "hello"
    if n>0: for line in recursive_f(n-1): yield line
    # the above line is the tricky one, you might be tempted to
    # yield recursive_f(n-1) # but it won't work.

Both are equivalent, one way you can call them is:

for yield_statement in gen_f(10): print yield_statement
Community
  • 1
  • 1
Andy Hayden
  • 359,921
  • 101
  • 625
  • 535