recursionfunctional-programmingsml

Could I ask for physical analogies or metaphors for recursion?


I am suddenly in a recursive language class (sml) and recursion is not yet physically sensible for me. I'm thinking about the way a floor of square tiles is sometimes a model or metaphor for integer multiplication, or Cuisenaire Rods are a model or analogue for addition and subtraction. Does anyone have any such models you could share?


Solution

  • Imagine you're a real life magician, and can make a copy of yourself. You create your double a step closer to the goal and give him (or her) the same orders as you were given.

    Your double does the same to his copy. He's a magician too, you see.

    When the final copy finds itself created at the goal, it has nowhere more to go, so it reports back to its creator. Which does the same.

    Eventually, you get your answer back – without having moved an inch – and can now create the final result from it, easily. You get to pretend not knowing about all those doubles doing the actual hard work for you. "Hmm," you're saying to yourself, "what if I were one step closer to the goal and already knew the result? Wouldn't it be easy to find the final answer then ?" (*)

    Of course, if you were a double, you'd have to report your findings to your creator.

    (also, I think I saw this "doubles" creation chain event here, though I'm not entirely sure).


    (*) and that is the essence of the recursion method of problem solving.

    How do I know my procedure is right? If my simple little combination step produces a valid solution, under assumption it produced the correct solution for the smaller case, all I need is to make sure it works for the smallest case – the base case – and then by induction the validity is proven!

    Another possibility is divide-and-conquer, where we split our problem in two halves, so will get to the base case much much faster. As long as the combination step is simple (and preserves validity of solution of course), it works. In our magician metaphor, I get to create two copies of myself, and combine their two answers into one when they are finished. Each of them creates two copies of themselves as well, so this creates a branching tree of magicians, instead of a simple line as before.

    One such example is escaping a maze, or e.g. finding the shortest escape route's length. When arriving at a junction chamber, we simply send along copies of ourselves, one for each exit in front of us (the divide step). If the goal is to escape, at least one of the copies for at least one junction will indeed escape the maze, if the maze has an exit at all. And if the goal is finding the escape route's length, we simply listen to what each returning copy has to say to us, and then compose our report and pass it on to our maker (the conquer/combine step).

    Another physical example is a Russian Matryoshka doll which is (an empty shell of) a doll that possibly contains a Matryoshka doll. The smallest innermost doll shell is empty.

    More about recursion here (accumulators) and here (coin change).


    Another, more conventional way to look at this is with only one worker who copies the execution recipes i.e. function definitions as needed, working along the input program.

    In this analogy we are sitting at a desk, with an inexhaustible supply of empty sheets of paper at the side. We also have some execution recipes written each on its piece of paper, somewhere, accessible as needed, and one "main" recipe, the one for the "program" to be executed, on its sheet of paper.

    Placing the program recipe in front of us, we follow it along, making notes at the margins as to the variables' values. Encountering a function call, we proceed to note the point of call at the margin, then copy that function's recipe onto a fresh empty sheet of paper and place that copy on top the current one in front of us, and follow it along from the top.

    Thus a stack of sheets is forming in front of us. As we finish up with a recipe's execution, we note its return value, then discard the sheet of paper and write that value down at the place which is expecting it at the call site, then continue with the recipe currently in front of us. When there are no more recipes to follow, the execution stops.

    One can immediately notice that it does not matter whether the recipe we're copying to the fresh blank sheet of paper is the same recipe we are currently following (recursion) or one which we have followed a few sheets down (mutual recursion). The new copy is fresh and new, independent of any other, with its own margins -- we do copy the original recipe, not any of its copies inside the stack.


    Another example is the Sierpinski triangle which is a figure that is built from three quarter-sized Sierpinski triangles simply, by stacking them up at their corners.

    Each of the three component triangles is built according to the same recipe.

    Although it doesn't have the base case, and so the recursion is unbounded (bottomless; infinite), any finite representation of S.T. will presumably draw just a dot in place of the S.T. which is too small (serving as the base case, stopping the recursion).

    There's a nice picture of it in the linked Wikipedia article.

    Recursively drawing an S.T. without the size limit will never draw anything on screen! For mathematicians recursion may be great, engineers though should be more cautious about it. :)

    Switching to corecursion ⁄ iteration (see the linked answer for that), we would first draw the outlines, and the interiors after that; so even without the size limit the picture would appear pretty quickly. The program would then be busy without any noticeable effect, but that's better than the empty screen.