Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the definition of "natural recursion"?

The Third Commandment of The Little Schemer states:

When building a list, describe the first typical element, and then cons it onto the natural recursion.

What is the exact definition of "natural recursion"? The reason why I am asking is because I am taking a class on programming language principles by Daniel Friedman and the following code is not considered "naturally recursive":

(define (plus x y)
    (if (zero? y) x
        (plus (add1 x) (sub1 y))))

However, the following code is considered "naturally recursive":

(define (plus x y)
    (if (zero? y) x
        (add1 (plus x (sub1 y)))))

I prefer the "unnaturally recursive" code because it is tail recursive. However, such code is considered anathema. When I asked as to why we shouldn't write the function in tail recursive form then the associate instructor simply replied, "You don't mess with the natural recursion."

What's the advantage of writing the function in the "naturally recursive" form?

like image 649
Aadit M Shah Avatar asked Aug 27 '15 22:08

Aadit M Shah


People also ask

What is recursion in real life?

In programming terms, recursion happens when a function calls itself. If you have a problem that is too complex, you can use recursion to break it down into simpler blocks. You do this in real life all the time. Imagine you have a whole box full of $100 bills and you need to count how much money you have.

What is the definition of recursion in programming?

In computer science, recursion is a programming technique using function or algorithm that calls itself one or more times until a specified condition is met at which time the rest of each repetition is processed from the last one called to the first.

What is a good example of recursion?

A classic example of recursion For example, factorial(5) is the same as 5*4*3*2*1 , and factorial(3) is 3*2*1 .

How do you explain simple recursion?

Recursion is a method of solving problems in which the solution relies on a simpler instance of the problem. As opposed to iteration, which attempts to build up to a solution, recursion aims to break a problem down to its most basic form.


3 Answers

"Natural" (or just "Structural") recursion is the best way to start teaching students about recursion. This is because it has the wonderful guarantee that Joshua Taylor points out: it's guaranteed to terminate[*]. Students have a hard enough time wrapping their heads around this kind of program that making this a "rule" can save them a huge amount of head-against-wall-banging.

When you choose to leave the realm of structural recursion, you (the programmer) have taken on an additional responsibility, which is to ensure that your program halts on all inputs; it's one more thing to think about & prove.

In your case, it's a bit more subtle. You have two arguments, and you're making structurally recursive calls on the second one. In fact, with this observation (program is structurally recursive on argument 2), I would argue that your original program is pretty much just as legitimate as the non-tail-calling one, since it inherits the same proof-of-convergence. Ask Dan about this; I'd be interested to hear what he has to say.

[*] To be precise here you have to legislate out all kinds of other goofy stuff like calls to other functions that don't terminate, etc.

like image 194
John Clements Avatar answered Oct 19 '22 20:10

John Clements


The natural recursion has to do with the "natural", recursive definition of the type you are dealing with. Here, you are working with natural numbers; since "obviously" a natural number is either zero or the successor of another natural number, when you want to build a natural number, you naturally output 0 or (add1 z) for some other natural z which happens to be computed recursively.

The teacher probably wants you to make the link between recursive type definitions and recursive processing of values of that type. You would not have the kind of problem you have with numbers if you tried to process trees or lists, because you routinely use natural numbers in "unnatural ways" and thus, you might have natural objections thinking in terms of Church numerals.

The fact that you already know how to write tail-recursive functions is irrelevant in that context: this is apparently not the objective of your teacher to talk about tail-call optimizations, at least for now.

The associate instructor was not very helpful at first ("messing with natural recursion" sounds as "don't ask"), but the detailed explanation he/she gave in the snapshot you gave was more appropriate.

like image 42
coredump Avatar answered Oct 19 '22 21:10

coredump


(define (plus x y)
(if (zero? y) x
    (add1 (plus x (sub1 y)))))

When y != 0 it has to remember that once the result of (plus x (sub1 y)) is known, it has to compute add1 on it. Hence when y reaches zero, the recursion is at its deepest. Now the backtracking phase begins and the add1's are executed. This process can be observed using trace.

I did the trace for :

(require racket/trace)
(define (add1 x) ...)
(define (sub1 x) ...)
(define (plus x y) ...)
(trace plus)

(plus 2 3)

Here's the trace :

>(plus 2 3)
> (plus 2 2)
> >(plus 2 1)
> > (plus 2 0)  // Deepest point of recursion
< < 2           // Backtracking begins, performing add1 on the results
< <3
< 4
<5
5               // Result

The difference is that the other version has no backtracking phase. It is calling itself for a few times but it is iterative, because it is remembering intermediate results (passed as arguments). Hence the process is not consuming extra space.


Sometimes implementing a tail-recursive procedure is easier or more elegant then writing it's iterative equivalent. But for some purposes you can not/may not implement it in a recursive way.

PS : I had a class which was covering a bit about garbage collection algorithms. Such algorithms may not be recursive as there may be no space left, hence having no space for the recursion. I remember an algorithm called "Deutsch-Schorr-Waite" which was really hard to understand at first. First he implemented the recursive version just to understand the concept, afterwards he wrote the iterative version (hence manually having to remember from where in memory he came), believe me the recursive one was way easier but could not be used in practice...

like image 1
Kevin Avatar answered Oct 19 '22 21:10

Kevin