Live data from Hacker News

Understanding Recursion as an Absolute Beginner

bigomega.dev

41–50 of 55 posts

Re: Understanding Recursion as an Absolute Beginner

#41
To anyone trying to understand recursion, I advice to learn basic Haskell. This is the language where recursion actually "clicked" for me.

All you need is very basic Haskell. You don't need functors, applicatives or monads. You only need simple functions, pattern matching, and maybe some parametric polymorphism to simplify the examples and make them more general:

  fliplist :: [a] -> [a]
  fliplist [] = []
  fliplist (x:xs) = fliplist xs ++ [x]

  l = [1,2,3,4,5,6,7]
  f = fliplist l

  main = print f

Re: Understanding Recursion as an Absolute Beginner

#42
post #23

Is there a good example of a time where recursion would be much more efficient than iteration?

not necessarily efficient but here’s my latest use case: scheduling a health check job recursively. if the health check fails, it schedules itself to run again in X seconds with N-1 runs until giving up and marking the service as down. iteration here simply wouldn’t work

Why not?

Just schedule a single health check with a loop that tries to health-check N times in a loop, with a delay in between. If the check succeeds at any point, return 'healthy' early.

Or schedule N health checks at different times, if any check succeeds cancel all others, etc. etc.

Re: Understanding Recursion as an Absolute Beginner

#44
post #22

Earlier quoted context omitted.

Thank you! This immediately stood out to me as a clever hack that makes it more complicated than it needs to be. I'd have written this function as: function countDownFrom(n) { console.log(n) if (n > 0) { countDownFrom(n - 1) } } This removes both the decrement operator and the return keyword, both of which distract from the concept being taught. This would also make it simple to add a `step` argument, which could be…

the only problem with this approach is it doesn’t clearly teach the concept of a base case and a recursive case

Exactly. That is what I wanted to cover in the blog as well. I could give a better or a difficult example too but that would make it overly complicated which I didn't want it to be.

Re: Understanding Recursion as an Absolute Beginner

#46

How "absolute beginner" are we talking here? At what point have we distilled a concept to its bare minimum and failure to understand simply requires the person to spend more time with the concept? If recursion is difficult to grasp, I'm afraid it's not going to get any easier from there.

I have clearly mentioned that if you can declare and call function in the language you are using then you're good enough to go through the blog. That answers your "How absolute beginner" question.

The reason why I wrote it is because of my own personal experience. In my programming class, my friends were having hard time understanding recursion so that was the primary motivation for writing this blog.

Re: Understanding Recursion as an Absolute Beginner

#47
post #16

I think the simplest way to understand recursion is as a for loop where you use the stack as the counter. Recursion is basically implementing the operations of a repetitive loop but the loop controls are not explicit like in a for loop, instead you use the stack as the counter of the loop. Once you think of recursion as just another way to do for loops, it immediately is demystified.

> Once you think of recursion as just another way to do for loops, it immediately is demystified.

I actually think this is not a good way to understand recursion. It's like saying "once you think of the lambda calculus as another way to build a Turing machine, it immediately is demystified." Yes, it's true that they're equally powerful, but their differences are what make one or the other more suitable to certain circumstances.

I think with recursion the best way to show this distinction is to look at navigation over a recursive structure, like a binary tree. You could implement any navigation over a binary tree with some nice loops, but they don't really mean anything in relation to the program: they're just a procedure to get the job done.

On the other hand, since binary trees are recursively defined (each node is either a leaf containing data or a branch containing two binary trees), using structural recursion to navigate the tree is significantly more straightforward than any looping construct. This is made super clear in a language like Haskell:

    data BTree
      = Leaf Int
      | Branch Int BTree BTree
    
    sumTree :: BTree -> Int
    sumTree (Leaf v) = v
    sumTree (Branch v l r) = v + (sumTree l) + (sumTree r)
But in, say, Python:

    @dataclass
    class BTree: pass
    
    @dataclass
    class Leaf(BTree):
      v: int
    
    @dataclass
    class Branch(BTree):
      v: int
      l: BTree
      r: BTree
    
    def sum_tree(t: BTree) -> int:
      stack = [t]
      total = 0
      while stack:
        curr = stack.pop()
        if isinstance(curr, Leaf):
          total += curr.v
        elif isinstance(curr, Branch):
          total += curr.v
          stack.append(curr.l)
          stack.append(curr.r)
        else:
          raise RuntimeError("Invalid BTree subclass.)
      return total
The Python solution isn't obviously correct. You'll have to read through that and make sure I implemented my traversal correctly — assuming you know what an iterative traversal looks like and you can recognize it without reading the code more fully first.

In contrast, the Haskell solution is clearly correct at a glance, I think. Ignoring the difference in length of code, the Haskell is clearly recursing over the structure of the tree. This is, in my opinion, significantly easier to reason about than any amount of looping.

I guess the caveat to all this is: recursion is well-suited for certain kinds of problems (such as navigating a recursive datatype), and loops are well-suited for different kinds of problems. But saying "they're equivalent so think of all recursion as loops" is missing the forest for the trees, I think.

Re: Understanding Recursion as an Absolute Beginner

#48

How "absolute beginner" are we talking here? At what point have we distilled a concept to its bare minimum and failure to understand simply requires the person to spend more time with the concept? If recursion is difficult to grasp, I'm afraid it's not going to get any easier from there.

> If recursion is difficult to grasp, I'm afraid it's not going to get any easier from there.

I strongly disagree. Many people have never had to reason about recursion so explicitly before going into CS. Just because it's difficult to understand explicitly at first doesn't mean they won't get better.

If they never understand recursion, then that's a problem, but I think that probably says more about the teacher than the student.

Re: Understanding Recursion as an Absolute Beginner

#49

The problem with recursion (and with the counting example in the post) is that a student will ask "why can't I do this with a loop?" It's better to use a problem where recursion MUST be used, such as a binary tree: The size (# of nodes) of a binary tree is: - 0, if the tree is empty, or - size of left subtree + size of right subtree + 1 If you draw a tree, any student will agree that the recursive method makes sense.…

True. But then you need to first teach the student about pointers/references, linked lists, and then binary trees. The point of this article was to explain recursion to an absolute beginner.

True, you need pointers to implement binary trees. But you just need chalk to draw one, and to explain a recursive algorithm that acts on it.

Re: Understanding Recursion as an Absolute Beginner

#50

The problem with recursion (and with the counting example in the post) is that a student will ask "why can't I do this with a loop?" It's better to use a problem where recursion MUST be used, such as a binary tree: The size (# of nodes) of a binary tree is: - 0, if the tree is empty, or - size of left subtree + size of right subtree + 1 If you draw a tree, any student will agree that the recursive method makes sense.…

Just a nitpick, loop languages and recursive languages have the same expressive power. Therefore you can write a loop program for any recursive one. So there is no "must".

Fair enough. I should have said "is most appropriate" rather than "must be used".
Post reply on HN