Question

EDIT: Thanks to everyone. I'm new to the language(just started using it two days ago), so that's why I'm unfamiliar with conds. I may rewrite it if I have time, but I just wanted to make sure I had the basic logic right. Thanks again!

My assignment is to make a tail-recursive function that removes the nth element from the list, 1 <= n <= listlength, with only two parameters, list x and element n. So, (remove 1 '(a b c d)) will return (b c d). I have written the following, and would like some reassurance that it is indeed tail recursive. The only thing I'm fuzzy on is if the recursive call can be nested inside an IF statement.

(define (remove n x)
  ; if n is 1, just return the cdr 
  (if (and (not (list? (car x))) (= n 1))
     (cdr x)
     ; if the car is not a list, make it one, and call recursively
     (if (not (list? (car x)))
        (remove (- n 1) (cons (list (car x)) (cdr x)))
        ; if n !=1, insert the cadr into the list at the car.
        ; Else, append the list at the car with the cddr
        (if (not(= n 1))
           (remove (- n 1) (cons (append (car x) (list(cadr x))) (cddr x)))
           (append (car x) (cddr x))))))
Was it helpful?

Solution

Yes, the procedure is tail-recursive, meaning: wherever a recursive call is performed, it's the last thing that happens in that particular branch of execution, with nothing more to do after the recursion returns - hence, we say that the recursive call is in tail position.

This can be clearly seen if we rewrite the procedure using cond instead of nested ifs, here you'll see that every branch of execution leads to either a base case or a recursive case, and all recursive calls are in tail position:

(define (remove n x)
         ; base case #1
  (cond ((and (not (list? (car x))) (= n 1))
         ; return from base case, it's not recursive
         (cdr x))
         ; recursive case #1
        ((not (list? (car x)))
         ; recursive call is in tail position
         (remove (- n 1) (cons (list (car x)) (cdr x))))
         ; recursive case #2
        ((not (= n 1))
         ; recursive call is in tail position
         (remove (- n 1) (cons (append (car x) (list(cadr x))) (cddr x))))
         ; base case #2
        (else
         ; return from base case, it's not recursive
         (append (car x) (cddr x)))))

For a more technical explanation of why the consequent/alternative parts of an if special form can be considered tail-recursive, take a look at section 3.5 of the current draft of the Revised^7 Report on the Algorithmic Language Scheme - the language specification, here's a link to the pdf file (in essence the same considerations apply to R5RS, it's just that they're explained in more detail in R7RS). In particular:

If one of the following expressions is in a tail context, then the subexpressions shown as ⟨tail expression⟩ are in a tail context

...

(if ⟨expression⟩ ⟨tail expression⟩ ⟨tail expression⟩)

(if ⟨expression⟩ ⟨tail expression⟩)

OTHER TIPS

Here is the Scheme specification on the tail recursion position for syntactic forms: enter image description here

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