How does Scheme return the "built up" value of a recursive procedure after the base case is met?

StackOverflow https://stackoverflow.com/questions/21599110

  •  07-10-2022
  •  | 
  •  

Question

I'm going through Simply Scheme and just got to the section on recursion.

I don't understand why when the base case is met Scheme returns the "built up" value of the recursive procedure and not the actual argument value which caused the base case to evaluate to #t.

For example, have a look at this sample code snippet of a recursive procedure which takes a word as input, reverses it, and spits it back out:

(define (reverse wd)
  (if (empty? wd) wd
      (word (last wd) (reverse (bl wd)))))

Here's what confuses me: (if (empty? wd) wd

I understand that when the actual argument value for the formal parameter wd is empty (or "") the base case evaluates to #t, causing the value of the second argument to if to be evaluated.

What I don't understand is how the second argument to if (wd, in this case) can return something that's not empty even though it would appear to be the same, empty, formal parameter that triggered the base case.

What am I missing?

If there's something in the documentation (or the text) which explains this I'd be happy to review it.

Was it helpful?

Solution

Since state doesn't change here you could just substitute what the function does with its recursion. Imagine you have a two letter word a :

(reverse a) ; =>
(word (last a) (reverse (bl a))) ; ==>
(word (last a) (word (last (bl a)) (reverse (bl (bl a))))) ; ==>
(word (last a) (word (last (bl a)) (bl (bl a)))) ; ==>
(word (last a) (word (last (bl a)) '()))

The last (bl (bl a)) is the only thing done by the last iteration and it's '(). The rest is done as the continuation of the previous calls.

EDIT

To clarify.. Imagine a is "ab"

(reverse "ab") ; turns into
(word (last "ab") (reverse (bl "ab")))           ; tuns into
(word "b" (reverse (bl "ab")))                   ; turns into 
(word "b" (reverse "a")))                        ; turns into 
(word "b" (word (last "a") (reverse (bl "a"))))) ; turns into
(word "b" (word "a" (reverse ""))))

Now look at that last one.. When (reverse "") returns "" it's the result used in the previous form call, in the word form, so you'll get (word "a" "") ==> "a", then that will be returned to the first call that does (word "b" "a"). These are continuations.

OTHER TIPS

The key insight is that it is returning empty for the base case. That's because the reverse of an empty string is an empty string.

The recursive cases actually work on that assumption. In words, here's how your reverse function works:

  1. If the given word is empty, return empty.
  2. Otherwise, take the last letter from the word, and join that with the reverse of the remainder of the word.

Example. Let's say we're reversing "Devin":

  • The word "Devin" isn't empty, so we will join "n" with the reverse of "Devi".
    • The word "Devi" isn't empty, so we will join "i" with the reverse of "Dev".
      • The word "Dev" isn't empty, so we will join "v" with the reverse of "De".
        • The word "De" isn't empty, so we will join "e" with the reverse of "D".
          • The word "D" isn't empty, so we will join "D" with the reverse of "".
            • The word "" is empty, so we return "".
          • Here, we return "D" joined with "", which is "D".
        • Here, we return "e" joined with "D", which is "eD".
      • Here, we return "v" joined with "eD", which is "veD".
    • Here, we return "i" joined with "veD", which is "iveD".
  • Here, we return "n" joined with "iveD", which is "niveD".
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top