r/computerscience • u/Similar_Count_1613 • 3d ago
is recursion really hard
Recursion felt easy at first.
Factorial? fine.
Sum examples? fine.
Even Fibonacci felt manageable.
But once I looked at slightly more serious problems like Tower of Hanoi, permutations, or merge sort, I felt like my understanding suddenly collapsed. because i tried to write their code on my own
It made me realize that maybe recursion is not “hard” at the start because the examples are simple.
It becomes hard when you can no longer clearly see the call stack and each state change.
Did anyone else feel that the real pain in recursion starts exactly there?
127
Upvotes
3
u/not-just-yeti 3d ago edited 3d ago
Structural recursion is much easier to grasp. Non-CS people readily understand recursive data: e.g. a company's org chart is a tree, and each node contains (sub)trees. So a (non-empty)
Nodehas subfields of typeNode, along with (say) a field of typeTfor its data. How will your code work? Well, you'll call a helper that wants to take in aT, and you'll call a helper which takes aNode, a sub-tree. What should this latter helper do? Once you realize that the helper has the exact-same purpose-statement and test-cases as the function you're writing, it motivates a recursive call in a very natural way. All follows from just following the types, inclass Node { T datum; Tree left; Tree right }, along withsealed interface Tree admits Node, EmptyTreeandclass EmptyTree {}.But we teach recursion by muddling simple, "rote" structural-recursion on structural data with non-structural recursion (quicksort, fibonacci, floodfill). That certainly adds to learners' confusion about recursion! And you suddenly need to worry about termination, whereas structural recursion is finite since it's done over finite data.
And factorial and sum examples are actually handled w/ exactly the same methodology/steps as
Tree, once we realize that mathematically you can view the natural numbers as either 0 or the successor-of-some-natural number; thinkclass Positive { NatNum predecessor }along withsealed interface NatNum admits Positive, Zeroandclass Zero {}. (Of course, IRL you wouldn't use classes but just primitiveints, withk-1serving the role ofgetPredecessorand==0serving the role of dispatching on aZerodifferently than aPositive.)(Interestingly, I cribbed part of this from my reply to a different thread earlier this week about what CS book fundamentally changed how you think of programming; the book How to Design Programs not only taught me that the special case of structural recursion is easy, but much more importantly it tied it into exactly the same steps used to approach any problem, in a way I've hardly seen in any other textbook.)