views:

125

answers:

1

I've got a deeply recursive function that should in theory work well even with large inputs. The problem is at the time of writing I forgot that C# doesn't do tail-call optimization very well, if at all, so I get StackOverflowExceptions for any complex-enough input. The basic structure of the method is in two large methods, each calling the other.

public object Simplify(object param) {
    if (IsSimple(param))
        return param;
    else
        return Expand(param);
}

private object Expand(object param) {
    return Simplify(ProcessExpansion(param));
}

Both IsSimple and ProcessExpansion have a relatively fixed stack depth - the only problem is the recursion between Simplify and Expand. How would you go about reducing the stack depth here?

I was thinking of returning delegates that would calculate the result, but that seems like overkill - there must be a simpler way. This is my thought of a solution (it isn't very polished because I keep thinking I must be doing something horribly wrong here):

public object Simplify(object param) {
    object result = () => SimplifyInternal(param);
    while (result is Func<object>)
        result = ((Func<object>)result)();
    return result;
}

private object SimplifyInternal(object param) {
    if (IsSimple(param))
        return param;
    else
        return Expand(param);
}

private object Expand(object param) {
    return () => SimplifyInternal(ProcessExpansion(param));
}

So my two questions are:

  1. What is it that feels horribly wrong with this solution?
  2. Can you think of a better one?
+7  A: 

Isn't this just

while(!IsSimple(param))
    param = ProcessExpansion(param);
return param;

?

Brian
The point being, if A calls B only in tail position, and B calls A only in tail position, then you should be able to do a little massaging to merge the two methods and wrap a while loop around it and get the same semantics.
Brian
It is. The example is greatly simplified though - the functions are complicated enough as it is, and each does something completely different. There's also some polymorphism involved with the different types of values that param can have. I could combine them into a single function theoretically, but it would make them far harder to maintain.
configurator
Indeed. In that case, one possible avenue is to write those functions in F#. Another is to use a trampoline (looks like you were trying this). Yet another would to be to use threads (e.g. when the stack gets high, QueueUserWorkItem and block on a result). None of these options are exceedingly attractive; use the one you are comfortable with authoring and maintaining. Also, consider if you can "change the problem" to make the issue go away in the first place.
Brian
+1 Brian is correct. I would either write the function in F# or convert it to a loop, whichever is more maintainable (keeping your coworkers and replacements in mind as well as yourself).
Stephen Cleary