Search code examples
recursionfunctional-programmingschemerackettail-recursion

How to implement append procedure using tail recursion in Scheme/Racket?


For a long time I'm trying to implement the append procedure in Racket using tail recursion(iterative).

The best solution I did so far is this:

(define (my-reverse lst)
  (define (iter lst reversed)
    (if (null? lst)
        reversed
        (iter (cdr lst) (cons (car lst) reversed))))
  (iter lst '()))

(define (append-iter el lst)
  (my-reverse (cons el (my-reverse lst))))

(append-iter 4 '(1 2 3)) ; (1 2 3 4)

My question is whether there is a better a way of implementation?


Solution

  • Well, it depends on your definition of "better" :) . Your solution is simple, and you won't find a more straightforward way to write your own procedure to append an element at the end of a list, using tail recursion.

    My only comment is that my-reverse is doing the same as the built-in reverse procedure, which most certainly will be tail recursive, so you can simply write it as:

    (define (append-iter el lst)
      (reverse (cons el (reverse lst))))
    

    If you're ok with using continuation passing style, the following solution is also tail-recursive and it only depends on the most basic primitive procedures:

    (define (append-iter el lst)
      (append-cps lst (cons el '()) (lambda (x) x)))
    
    (define (append-cps lst1 lst2 k)
      (if (null? lst1)
          (k lst2)
          (append-cps
           (cdr lst1)
           lst2
           (lambda (appended-cdr)
             (k (cons (car lst1) appended-cdr))))))
    

    Either way, it works as expected:

    (append-iter 4 '(1 2 3))
    => '(1 2 3 4)
    

    If you're curious, the CPS solution will evaluate to an expression like the one shown below, which naturally leads to the answer:

    ((lambda (append-cdr)
       ((lambda (appended-cdr)
          ((lambda (appended-cdr)
             ((lambda (x) x)
              (cons 1 appended-cdr)))
           (cons 2 appended-cdr)))
        (cons 3 append-cdr)))
     '(4))
    => '(1 2 3 4)