views:

235

answers:

2

My issue is with a certain style of code that very much resembles recursion, but isn't quite that. Recursion is, to quote Wikipedia, "a method of defining functions in which the function being defined is applied within its own definition". Similarly mutual recursion applies another function which, directly or indirectly, applies the function we are defining.

The problem is that the code I am thinking of, and dealing with, does not use the same function! It uses the same code in another function (as a method or closure).

The problem here is that while my code is the same, the functions are not. Take a look a the following basic mutual recursion example:

def is_even(x):
    if x == 0:
        return True
    else:
        return is_odd(x - 1)

def is_odd(x):
    if x == 0:
        return False
    else:
        return is_even(x - 1)

This is somewhat intuitive, and very clearly mutually recursive. However, if I wrap each function up as an inner function that is created once every call, it gets less clear:

def make_is_even(): 
    def is_even(x):
        if x == 0:
            return True
        else:
           return make_is_odd()(x - 1)
    return is_even

def make_is_odd(): 
    def is_odd(x):
        if x == 0:
            return False
        else:
            return make_is_even()(x - 1)
    return is_odd

def is_even2(x):
    return make_is_even()(x)

def is_odd2(x):
    return make_is_odd()(x)

Disregarding optimizations like implicit memoization etc., this produces a chain of function calls that isn't strictly recursive, creating and calling various new functions without ever calling the same one twice. Nonetheless all these functions follow a common template, and are just the same function created over and over again (possibly with different free variables.

And again, we can come up with a directly equivalent (after all, classes are really just closures, right ;) implementation using classes. This is especially significant, since this style of [insert name here] is used in, e.g., the Composite Pattern. The difference is that with the Composite design pattern, and most uses (even of the closures), the instances are not usually created on the fly. It is still essentially the same.

class EvenChecker(object):
    def check(self, x):
        if x == 0:
            return True
        else:
            return OddChecker().check(x - 1)

class OddChecker(object):
    def check(self, x):
        if x == 0:
            return False
        else:
            return EvenChecker().check(x - 1)

def is_even3(x):
    return EvenChecker().check(x)

def is_odd3(x):
    return OddChecker().check(x)

This time the chain is of object creation and method calls, but the principle is the same. (I would actually note that it's slightly different, in that Python defines a simple wrapper on a per-object basis which itself calls the very same function every time-- but this is not necessarily something we need to know, and it doesn't need to be true for other implementations of classes and objects. But yes, strictly speaking it is mutually recursive, as well as... something more, and it's that other thing that I want to know.)

+1  A: 

Apparently, it is called Mutual Recursion :)

The article even gives the same example as you, with odd? and even? functions.

Justin Ethier
You have missed the crux of the question. It used the same example as my *first* example. That one I specifically labeled as "mutual recursion". It's the other ones I am concerned about.
Devin Jeanpierre
Yes, but your other examples still use the same fundamental idea.
Justin Ethier
yes, but with a twist: mutual recursion calls back to the same function, but these do not.
Devin Jeanpierre
No, they're *still* the same functions, you just access them through different (useless) instances each time.
Thomas Wouters
+1  A: 

As you point out, this is still mutual recursion. I don't think the "something more" you're asking about has a name; if it does I've never heard it.

David Seiler
The last example is only certainly mutual recursion because each method call in Python is against a wrapper (instance-method), which calls a function-- this function is the one you defined on your class, and there's only one. So the call chain might look like so: `[is_odd3 (3)] -> [instance-method[OddChecker.check] (3)] -> [OddChecker.check (self, 3)] -> ... --> [OddChecker.check (self, 1)] --> ... --> True`. This is mutually recursive-- it keeps calling itself. But, say, for the closures one, or for the same OO example but in E, the same function is never called again.
Devin Jeanpierre