13

I know what recursion is (when a patten reoccurs within itself, typically a function that calls itself on one of its lines, after a breakout conditional... right?), and I can understand recursive functions if I study them closely. My problem is, when I see new examples, I'm always initially confused. If I see a loop, or a mapping, zipping, nesting, polymorphic calling, and so on, I know what's going just by looking at it. When I see recursive code, my thought process is usually 'wtf is this?' followed by 'oh it's recursive' followed by 'I guess it must work, if they say it does.'

So do you have any tips/plans/resources for building up skills in this area? Recursion is kind of a weird concept so I'm thinking the way to tackle it may be equally weird and inobvious.

Anna S
  • 5
Andrew M
  • 1,091

9 Answers9

10

Start with something simple and trace it out with pencil and paper. Seriosuly. A good place to start is tree-traversal algorithms, since they are much more easily handled using recursion than regular iteration. It doesn't have to be a complicated example, but something that is simple and you can work with.

Yes, it's weird and sometimes counter-intuitive, but once it clicks, once you say "Eureka!" you'll wonder how you didn't understand it before! ;) I suggested trees because they are (IMO) the easiest structure to understand in recursion, and they are easy to work with using a pencil and paper. ;)

5

I strongly recommend Scheme, using the book The Little Lisper. Once you're done with it, you will understand recursion, deep down. Almost guaranteed.

4

I definitely suggest SICP. Also, you should check out the authors' introductory course videos here; they're incredibly mind-opening.

Another road, not so strictly related to programming, is reading Gödel, Escher, Bach: an Eternal Golden Braid by Hofstadter. Once you get trough it, recursion will look as natural as arithmetics. Also, you'll be convinced that P=nP and you're going to want to build thinking machines - but it's a side-effect that's so small when compared to the benefits.

cbrandolino
  • 2,009
  • 1
  • 18
  • 21
2

Essentially it just comes down to practice... Take general problems (sorting, searching, math problems, etc.) and see if you can see a way in which those problems can be solved if you apply a single function several times.

For example quick sort operates in that it moves element in a list into two halves and then applies itself again to each of those halves. When the initial sorting occurs its not worried about getting the two halves sorted at that point. Rather its taking the pivot element and putting all elements smaller than that element on one side and all elements larger than or equal to on the other side. Does this make sense how it can recursively call itself at that point to sort the two new smaller lists? They're lists too. Just smaller. But they still need to be sorted.

The power behind recursion is the divide and conquer notion. Break a problem repeatedly into smaller problems which are identical in nature but just smaller. If you do that enough eventually you get to a point where the only remaining piece is already solved then you just work your way back out of the loop and the problem is solved. Study those examples you mentioned until you understand them. It may take a while but eventually it will get easier. Then try to take other problems and make a recursive function to solve them! Good luck!

EDIT: I must also add that a key element to recursion is the guaranteed ability of the function to be able to stop. This means the break down of the original problem must continuously get smaller and eventually there needs to be a guaranteed stopping point (a point at which the new sub problem is either solvable or already solved).

Kenneth
  • 2,701
2

Personally I think your best bet is through practice.

I learnt recursion with LOGO. You could use LISP. Recursion is natural in those languages. Otherwise you can liken it to the study of mathematical suites and series where you express what's next based on what came before, i.e. u(n+1)=f(u(n)), or more complex series where you have multiple variables and multiple dependencies, e.g. u(n)=g(u(n-1), u(n-2), v(n), v(n-1)); v(n)=h(u(n-1), u(n-2), v(n), v(n-1))...

So my suggestion would be that you find simple (in their expression) standard recursion "problems" and try and implement them in your language of choice. Practice will help you learn how to think, read and express those "problems". Note that often some of these problems can be expressed through iteration, but recursion might be a more elegant way to solve them. One of those is the calculation of factorial numbers.

Graphical "problems" I find make it easier to see. So look up Koch's flakes, Fibonacci, the dragon curve and fractals in general. But also look at the quick-sort algorithm...

You need to crash a few programs (endless loops, tentative use of infinite resources), and mishandle ending conditions (to get unexpected results) before you get your head around it all. And even when you get it you will still make those mistakes, just less often.

asoundmove
  • 1,667
2

Structure and Interpretation of Computer Programs

It is the book used for learning, not just recursion, but programming in general at various universities. It is one of those foundational books that yields more information the more experience you gain and the more you read it.

0

As much as I like SICP and Gödel, Escher, Bach: an Eternal Golden Braid, Touretzky's LISP: A Gentle Introduction to Symbolic Computation also does a good job on introducing recursion.

The basic concept is this: First, you have to know when your recursive function is finished, so it can return a result. Then, you have to know how to take the unfinished case, and reduce it to something that you can recur on. For the traditional factorial(N) example, you're finished when N <= 1, and the unfinished case is N*factorial(N-1).

For a much uglier example, there's Ackermann's function A(m,n).

A(0,n) = n+1.                                   This is the terminal case.
A(m,0) = A(m-1,1) if m > 0.                     This is a simple recursion.
A(m,n) = A(m-1, A(m, n-1)) if m > 0 and n > 0.  This one is ugly.
0

I suggest playing around with some ML-style functional languages like OCaml or Haskell. I found that pattern-matching syntax really helped me understand even relatively complicated recursive functions, certainly much better than Scheme's if and cond statements. (I learned Haskell and Scheme at the same time.)

Here's a trivial example for contrast:

(define (fib n)
   (cond [(= n 0) 0]
         [(= n 1) 1]
         [else (+ (fib (- n 1)) (fib (- n 2)))]))

and with pattern matching:

fib 0 = 0
fib 1 = 1
fib n = fib (n - 1) + fib (n - 2)

This example does not really do the difference justice--I never had problems with either version of the function. It's just to illustrate what the two options look like. Once you get to much more complex functions, using things like lists and trees, the difference becomes much more pronounced.

I particularly recommend Haskell because it's a simple language with very nice syntax. It also makes much easier to work with more advanced ideas like corecursion:

fibs = 0 : 1 : zipWith (+) fibs (drop 1 fibs)
fib n = fibs !! n

(You won't understand the above code until you play a bit with Haskell, but rest assured that it's basically magical :P.) Sure you could do the same with streams in Scheme, but it's much more natural in Haskell.

0

It's out of print, but if you can find it, "Recursive Algorithms" by Richard Lorentz is about nothing but recursion. It covers the basics of recursion, as well as specific recursive algorithms.

The examples are in Pascal, but are not so large that the choice of language is bothersome.

Wayne Conrad
  • 1,148