
Thinking About Recursion - ColinWright
http://www.solipsys.co.uk/new/ThinkingAboutRecursion.html?HN_20170308
======
clusmore
Gerald Sussman covers the Tower of Hanoi problem in the SICP lectures [1]. The
thing I find fascinating about this solution is that it doesn't even require
the rule that you can't place larger discs on smaller discs - it simply is
never optimal to do so anyway. And the solution is so simple, I wonder if you
could invent a game by first coming up with a solution and then seeing what it
actually solves.

[1] [https://youtu.be/dlbMuv-jix8?t=47m17s](https://youtu.be/dlbMuv-
jix8?t=47m17s)

~~~
jawarner
Grant Sanderson has a beautiful video on the Towers of Hanoi problem too [1],
part of his animated math project.

[1]
[https://www.youtube.com/watch?list=PLZHQObOWTQDMRtm8h9bG9P06...](https://www.youtube.com/watch?list=PLZHQObOWTQDMRtm8h9bG9P06WINNoBnCR&v=2SUvWfNJSsM)

~~~
clusmore
Wow, fantastic insight. Thanks for sharing.

------
brianberns
Man, this article really shows why imperative programming is so much harder to
reason about than functional programming.

Difference between "variables" in math vs. functional programming? None. The
value of a variable can't change.

Difference between "function" in math vs. functional programming? None. It's
just a map from one set to another.

~~~
komali2
I don't really know much about functional programming. How can a variable not
change?

~~~
crimsonalucard
In functional programming the entire program is literally a single expression.
This is essentially what functional programming is. A variable in a single
expression cannot be modified.

------
jsperson
I just spent an hour reading this blog. I now feel absolutely smarter and
relatively dumber. What a wonderful blog. Thanks for sharing!

~~~
ColinWright
Wow. Thank you.

------
charles-salvia
The best way I've found to introduce the concept of recursion is to explain it
like trying to escape from a maze. Suppose at your starting position you have
3 possible corridors to try, in search of the exit. You mark your current
position (using bread crumbs or whatever), then try each corridor. If the
corridor is a dead end you return to where you left the bread crumb and try
another corridor. If one of the corridors leads you to a room with yet _more_
corridors, you leave _another_ bread crumb to mark your position, and then
recursively try each of those corridors. Each time you hit a dead end, you'll
be able to back track to the last crumb you left. Eventually, you'll find a
corridor that leads to the exit. (At that point the metaphor breaks a little
because stack unwinding would require you to go back and clean up all your
crumbs, but whatever. Maybe a better metaphor would be collecting some items
from all the corridors in the maze, and bringing them back to your starting
position.)

~~~
clusmore
My favourite analogy is that of finding out which row you are in at the
cinema. You don't know which row you are, but you know you're 1 behind the guy
in front of you, so you ask him and figure you'll just add 1 to that. He
doesn't know either, and using the same strategy he asks the person in front
of him.

Eventually, the guy in the second row asks the guy in the first row, who
immediately says he's in the first row. Then each person adds 1 to the answer
they get back and feed it back until eventually you get your answer.

~~~
charles-salvia
That's a good analogy - the problem is a student could reasonably ask "why
would you do it this way"? - i.e. this kind of algorithm seems like a better
candidate for iteration. Instead of asking the guy in front of you, get up and
count the rows. I know it's for teaching purposes of course, but I'd like the
analogy to involve a task that requires that you try something, then back
track to some earlier state, then try a different thing, etc.

Of course, I guess this just highlights how all the toy examples we see in CS
courses often use trivial things like computing Fibonacci sequences or
factorials, which are much easier to do iteratively. This often leaves
students wondering why anyone would ever even use recursion, or at best, makes
it seem like recursion vs. iteration is a mere stylistic choice.

~~~
clusmore
>the problem is a student could reasonably ask "why would you do it this way"?
- i.e. this kind of algorithm seems like a better candidate for iteration

Fantastic question. And a point I would respond with is that if you were to
actually get up and walk to the front of the cinema counting rows as you go,
you would realise that in doing so you would have walked forwards one row and
then performed exactly the same action that the person in front of you would
have done had they tried to count their row. So even this "iterative"
procedure parallels recursion.

Perhaps a problem that seems harder to convert to iteration is that of
evaluating arbitrary arithmetic expressions. In order to evaluate a binary
expression, you evaluate its two parts (recursively) and then perform the
binary operator on the two operands. In order to evaluate a numeric literal,
you simply take its value. So (1 + (2 * 3)) involves evaluating 1 to itself,
(2 * 3) to 6, and then finally (1 + 6) to 7.

~~~
dreamcompiler
Ordinary scalar multiplication can itself be expressed recursively, e.g. (2734
* 5896). Break each number into two parts and the answer is (27 * 96 * 100) +
(34 * 58 * 100) + (27 * 58 * 100 * 100) + (34 * 96).

Fine.

Now what is (27 * 96)? (2 * 6 * 10) + (7 * 9 * 10) + (2 * 9 * 10 * 10) + (7 *
6). etc. [In real life you'd do this in binary and the multiplies by 10 become
shifts.]

This is not usually the fastest way to do scalar multiplication, but it
illustrates that:

1\. Ordinary scalar multiplication is polynomial multiplication.

2\. Polynomial multiplication is convolution.

3\. And since we can [sometimes] speed up convolution by moving it to the
Fourier domain, we can multiply numbers by taking their FFTs first, doing an
elementwise multiplication, and then taking an inverse FFT. This actually pays
off on very large numbers.

Multiplication is fun stuff.

------
jonsen
"Let me see if I got this recursive disk juggling right. You move the top-most
disk to position C. Then recursion can move the rest to position B. And
finally I move the disk at position C on top of the rest at position B."

"No, no, no. Look ..."

"But to start you can only move the top-most disk?"

"Yes, but it won't work because ..."

"What? You _have_ to move the top-most as the first move."

------
ihoz
[https://www.google.com/search?q=recursion](https://www.google.com/search?q=recursion)

------
akkartik
I feel fairly confident that I've solved the problems of teaching functions
([http://akkartik.name/post/mu](http://akkartik.name/post/mu) ; particularly
the last couple of sections) and recursion
([http://akkartik.name/post/swamp](http://akkartik.name/post/swamp)). Tl;dr -
It just requires first introducing these ideas in a statement-oriented
language without recursive expressions. Allow learners to see the steps unfold
explicitly, one by one.

(Comment deliberately phrased provocatively to hear push-back and feedback.)

------
Koshkin
To those who might be under the impression that recursion is not all that
useful in the _practice_ of commercial software development (as opposed to a
purely academic interest or as a method of solving logical puzzles) I would
point out the fact that recursion is the only way to emulate iteration in
functional languages and, as such, it finds extensive use in the C++ template
language as well as XSLT - two most used pure functional programming languages
today.

~~~
brianberns
Many commonly-used data structures (e.g. trees) are nearly impossible to
manage without recursion.

~~~
crimsonalucard
Not true. A loop and a stack can achieve the same thing!

~~~
auxym
As an amateur programmer, I'm intrigued in how this is achieved, would you
happen to have any references you could point me to?

My first encounter with recursion was when I needed to traverse a directory
tree. I couldn't figure how to go arbritrarily deep without writing tens of
nested for-loops. Then recursion somehow hit me, or I found something
interesting on Google, can't remember, it was over 10 years ago, pretty sure
SO wasn't around.

...and then a while later I found out about os.walk (this was python).

~~~
clusmore
Here's a recursive directory traversal in Python:

    
    
      import os
      import os.path
      
      def list_files_recursive(path):
          filenames = os.listdir(path)
          for filename in filenames:
              fullname = os.path.join(path, filename)
              if os.path.isdir(fullname):
                  yield from list_files_recursive(fullname) # recursively explore folders
              else:
                  yield fullname
    

and here is the same program with an explicit stack

    
    
      import os
      import os.path
      
      def list_files_stack(path):
          folders = [path] # create stack of unexplored folders
          while folders: # repeat until stack is empty
              path = folders.pop() # pick a folder from stack
              filenames = os.listdir(path)
              for filename in filenames:
                  fullname = os.path.join(path, filename)
                  if os.path.isdir(fullname):
                      folders.append(fullname) # push folder to stack
                  else:
                      yield fullname

~~~
auxym
Wow, somehow I expected something complex.

Thanks a lot!

------
lutusp
Quote: "These are variables, and when I've been teaching people to program
sometimes I see the light dawn when they realise that a variable name is like
a box with the name on the outside."

Yes. Maybe there could be some algebra in advance of -- or alongside -- early
coding lessons, where this idea is central to both fields. One variable name,
any numeric value.

It's the same with functions, the lifeblood of analysis -- one function name,
any equation. So in a perfect world, people would be learning calculus and
general analysis while writing their first program functions -- indeed, they
might be learning higher math while coding their lesson materials at the same
time.

[http://arachnoid.com/calculus](http://arachnoid.com/calculus)

------
yazaddaruvala
Kinda off topic. And a kinda crazy hypothetical:

I would really like to see a production language, where program-stack based
recursion was a compile time error. Instead, only recursion as syntax sugar is
allowed. i.e. Only recursion (or any cyclic function call graph) that the
compiler can translate to "imperative code", should be allowed to compile.

For two reasons:

1\. I honestly dream of a world where we can no longer think about infinitely
propagating program-stacks.

2\. It would force us to invent better optimization algorithms (like tail-call
optimization) but for more general recursive patterns.

~~~
dreamcompiler
You're talking about continuation-passing style, which makes stacks
superfluous. It's a mind-blowing idea. But it's also a royal pain to write
code directly this way, so some compilers convert to continuations under the
sheets.

~~~
yazaddaruvala
Its crazy that this idea was invented in 1975[0], and I haven't heard of it,
even after:

\- 4 years of self learned programming

\- 4 years of university for computer engineering

\- 5 years in the industry, as a software engineer

[0] [https://en.wikipedia.org/wiki/Continuation-
passing_style](https://en.wikipedia.org/wiki/Continuation-passing_style)

~~~
dreamcompiler
There are two very different (yet of course functionally equivalent) paradigms
for general computation: Turing Machines and Lambda Calculus (Turing vs.
Church). All schools teach TMs but not all teach LC. Which is a shame, because
some wonderful insights like Functional Programming and Lisp and CPS come
mostly from the LC paradigm.

------
crooked-v
To think about recursion, first, think about recursion.

------
guiambros
Mandatory xkcd-clone: [http://thomaspark.co/2017/01/relevant-
xkcd/](http://thomaspark.co/2017/01/relevant-xkcd/)

Previous discussion:
[https://news.ycombinator.com/item?id=13793404](https://news.ycombinator.com/item?id=13793404)

------
kjhughes
[https://news.ycombinator.com/item?id=13819624](https://news.ycombinator.com/item?id=13819624)

~~~
mbrookes
I clicked it three times before I realized!

