0

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..?).

Community
  • 1
  • 1
user1915570
  • 347
  • 1
  • 6
  • 22
  • 2
    Woah. Try and break your question down into smaller, simpler, easy-to comprehend questions. Also be sure to use `inline code formatting` where applicable. This is hard to read. – Jonathon Reinhart May 09 '14 at 07:54
  • read [this answer](http://stackoverflow.com/a/23372527/849891) first. Do you understand everything in it? – Will Ness May 09 '14 at 18:55
  • "call to recursive local procedure" means, a call to a local procedure, which (procedure) is recursive. – Will Ness May 09 '14 at 22:08

1 Answers1

2

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.

Sylwester
  • 44,544
  • 4
  • 42
  • 70
  • I tried your non tail-recursive version. then I get this " define-values: assignment disallowed; cannot change constant constant: vector->list " – user1915570 May 09 '14 at 13:13
  • @user1915570 Your Scheme implementation doesn't like you redefining library procedures. I've changed the names to postfix with iter for tail recursive and rec for non tail recursive one. – Sylwester May 09 '14 at 13:59
  • isn't there `do` in Scheme? – Will Ness May 09 '14 at 18:08
  • 1
    @WillNess It's derived syntax. [In the report](http://www.r6rs.org/final/html/r6rs-lib/r6rs-lib-Z-H-6.html#node_idx_258) you'll se it's just a macro and it uses tail recursion to do the job. I'm not very fond of `do` and prefer named `let` which is the same but it doesn't hide the recursion. Just like the magic of `loop` in CL you have to admire Scheme purity. – Sylwester May 09 '14 at 20:04
  • I've read the link you provided, and it says "The when and unless expressions are derived forms" but it doesn't say this about `do`. It shows a macro definition for `do` but that may be just to serve as a description. – Will Ness May 09 '14 at 22:07
  • @WillNess It's marked library syntax in earlier reports and giving a definition of `do` is a strong indication it still is. The requirement of implementations to do TCO makes the expansion of `do` just as good as any primitive implementation. Of course, many Scheme implementations do implement a part of library syntax and library procedures as primitives for various reasons and it's not like CL where moving something from being a macro to a primitive makes a `macroexpand` challenge. – Sylwester May 10 '14 at 03:51