Question

Originally I posted one question "understanding a tail-recursive vector->list answer" and this is additional questions. my general understanding in scheme is really vague. so I have now several more questions:

;;;;;; original code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define vector->list:rec
 (lambda (v)
  (letrec ((helper
      (lambda (vec r i)
        (if (< i 0) 
            r
            (helper vec (cons (vector-ref v i) r) (- i 1))  ;; Q1
            ))))
    (if (> (vector-length v) 0)  ;; line 9
      (helper v                  ;; line 10 
              (cons (vector-ref v (- (vector-length v) 1)) '()) 
              (- (vector-length v) 2))
      '()))))

Q2) tail recursion which makes me really difficult to understand. I understood why they need tail recursion and basically they use it to avoid iteration, so they use helper as intermediate routine.. so they can avoid put each iteration into stack.... something like this. and letrec/lambda expression like here:

;;;;;;;;;letrec/lambda express;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (some-procedure...)
  (letrec ((helper (lambda (x) 
               ...
               (if some-test?
                   (helper ...))))) ; recursive call --- Q2-1
       ...
    (helper ...)  ; call to recursive local procedure  ---- Q2-2
  ...))

line Q2-2: why this is 'local recursive'? 'local' sounds to me that recursive for intermediate routine...here intermediate means that my understanding..

[my confusion] tail recursive is should not iterate(calls) itself until at the end of the entire program - so shouldn't be in the intermediate routine.. = shouldn't be inside helper? based on my understanding until now... helper is for intermediate routine which encapsulated in letrec expression..?.) so only calls itself in the end.(my meaning..: outside letrec..?).

Was it helpful?

Solution

First off, I would have rewritten you example a little, to this:

(define (vector->list-iter v)  
  (let loop ((i (- (vector-length v) 1)) (acc '()))
    (if (< i 0)
        acc
        (loop (- i 1) (cons (vector-ref v i) acc)))))

And to see the difference lets make the non tail-recursive version:

(define (vector->list-rec v)
  (define len (vector-length v))
  (let loop ((i 0))
    (if (>= i len)
        '()
        (cons (vector-ref v i) (loop (+ i 1))))))

There is no loop functionality in Scheme. It's only recursion and recursion that does not grow the stack, because there is more to do in the previous step, is called tail recursion.

Since we can iterate a vector in any way (it's O(1) access) we can iterate it last to first or first to last. Since a list can only be made last to first the non tail-recursive version won't apply cons on the first element until it has made the whole list except the first element. This makes a 5 element vector to have 5 continuations when hitting the base case. If it were a large vector it can result in a stack overflow.

The first example make the list consisting of the last element first and when it's done it recurses. It does not need to cons anything since the consing was done before the recursion. It's not every problem that can be dealt with like this. Imagine you want to copy a list. It can be iterated from beginning to end but built up from end to beginning. Without mutation or extra consing there is no way to make such procedure tail recursive.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top