REVERSE function in LISP

2019-08-13 19:32发布

问题:

Could anybody explain in detail how the following pure LISP function works:

(DEFINE (REVERSE (LAMBDA (L)
        (REV NIL L))))
(DEFINE (REV (LAMBDA (OUT IN)
        (COND ((NULL IN) OUT)
        (T (REV (CONS (CAR IN) OUT) (CDR IN))))))

The function should reverse the order of the elements in a list, that's clear, but I am still unable to understand how it works.

*EDIT*

okay i believe i figured it out. The REVERSE function is called with a list as argument, and calls a REV function with NIL and that list L as arguments.

REV: OUT is bound to NIL and IN is bound to the list L. Now, if the first argument (IN) is empty (NULL?) we are done and we can return OUT, which is the list, successfully reversed. Otherwise we have to continue by recursively calling REV with OUT+the first element of IN as first argument and the rest of IN as the second argument. Is this how it works?

The only question is: What's about the LAMBDA thing here?

回答1:

Yes, it is how it works. This is known as accumulator argument technique for writing tail recursive functions. It helps to visualize its operation with some example, e.g.

reverse [1,2,3,4] 
  = rev  NIL  [1,2,3,4]
  = rev  [1]    [2,3,4]
  = rev  [2,1]    [3,4]
  = rev  [3,2,1]    [4]
  = rev  [4,3,2,1]  NIL
  = [4,3,2,1]

It seems your code comes from an oldish (1st ed. 1982, ISBN 0-471-08755-6) textbook. In today's Scheme e.g. it's written almost the same, but without the extra parentheses (with few constants redefined):

(define reverse (lambda (l) .... ))
(define rev (lambda (out in) .... ))

(define NIL '())
(define T    #t)
(define NULL null?)

The so-called "lambda forms", i.e. forms (well-formed, parenthesized snippets of code) starting with lambda "keyword", signify anonymous functions. The list after the symbol lambda specifies formal parameters of such function. define allows us to name the function created by a lambda-form. That way we can make recursive call to it inside its definition, using its name.

This recursive call is in tail position, so the operation of this function is equivalent to a loop (achieved by reusing the stack frame for the function call, resetting the formal parameters on each iteration which thus serve as loop variables).