Sergey
Sergey

Reputation: 11908

Return list without last element in common lisp

I wrote my silly function which returns a list without the last element in common lisp. Is there any more elegant solution to this problem?

Here's my code:

(defun list-without-last (l)
  (if (> (length (rest l)) 0)
      (append (list (first l)) (list-without-last (rest l)))
      nil))

Upvotes: 6

Views: 14454

Answers (6)

Short and simple, just like Lisp. Here is the magic stuff:

(defun without-last(l)
    (reverse (cdr (reverse l))))

Upvotes: 14

mihai
mihai

Reputation: 4722

(defun remove-last (lst)
  (do ((l lst (rest l))
       (res '()))
      ((null (rest l)) (nreverse res))
    (push (first l) res)))

Upvotes: 0

Nepumbra
Nepumbra

Reputation: 1

What about:

(defun butlast2 (L)
  (if (null (rest L))
    nil
    (cons (first L) (butlast2 (rest L)))
  )
)

Upvotes: 0

ghollisjr
ghollisjr

Reputation: 153

As Rainer Joswig mentioned above, you should use the common lisp built-in function butlast.

But, if you still want to see what an efficient recursive version would look like:

(defun butlast2 (list)
  (labels ((butlast2-worker (list result)
             (if (null list)
                 (nreverse result)
                 (let ((element (first list))
                       (rest (rest list)))
                   (if (null rest)
                       (nreverse result)
                       (butlast2-worker rest (cons element result)))))))
    (butlast2-worker list ())))

As long as your lisp implementation supports tail call optimization, this will be converted into a loop. The trick is that whenever butlast2-worker is called, its result will be returned directly, meaning that you don't need to keep track of the arguments/internal variables from previous calls of the function. Which lastly means that you don't need to keep filling the call stack as you would normally do for a recursive function.

Looking at Rainer Joswig's definition, you can see a tremendous difference in size. Behold the power of loop, and learn to use it wisely whenever you can (or better: use iterate http://common-lisp.net/project/iterate/).

Upvotes: 0

user797257
user797257

Reputation:

Sometimes you may find yourself needing to modify the list in place rather than making a copy, in which case this may be handy:

(defun butlast! (x)
  (do ((y x (cdr y)))
      ((null (cddr y))
       (and (rplacd y nil) (return x)))))

Upvotes: 2

Rainer Joswig
Rainer Joswig

Reputation: 139251

Your function has two problems:

  • you are using LENGTH. LENGTH has to scan the whole list.

  • you are using APPEND. Try to use CONS. CONS is simpler.

Common Lisp also already provides this function. It is called BUTLAST.

In real code we also would not use recursion. The stack size would limit the length of lists we can process.

An iterative version using the LOOP macro:

CL-USER> (defun my-butlast (list)
           (loop for l on list
                 while (rest l)
                 collect (first l)))
MY-BUTLAST                                                                                                                                      
CL-USER> (compile 'my-butlast)
MY-BUTLAST                                                                                                                                      
NIL                                                                                                                                             
NIL                                                                                                                                             
CL-USER> (my-butlast '(1 2 3 4 5))
(1 2 3 4)                                                                                                                                       
CL-USER> (my-butlast '(1))
NIL                                                                                                                                             
CL-USER> (my-butlast '(1 2))
(1)                                                                                                                                             

Upvotes: 9

Related Questions