2012-05-17 3 views
4

Я написал свою глупую функцию, которая возвращает список без последнего элемента в общем lisp. Есть ли более элегантное решение этой проблемы?Список возвратов без последнего элемента в общем lisp

Вот мой код:

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

ответ

7

Ваша функция имеет две проблемы:

  • вы используете ДЛИНЫ. LENGTH должен отсканировать весь список.

  • Вы используете APPEND. Попробуйте использовать CONS. CONS проще.

Общий Lisp также предоставляет эту функцию. Он называется BUTLAST.

В реальном коде мы также не будем использовать рекурсию. Размер стека ограничивал бы длину списков, которые мы можем обрабатывать.

итерационный вариант с использованием LOOP макроса:

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)                                    
+0

Большое спасибо за butlast, кстати, что проблема с Append? – Sergey

+0

'APPEND' должен отсканировать весь список, а также скопировать все элементы. Если вы вызываете 'APPEND' несколько раз в цикле, вы получаете функцию, которая экспоненциально медленнее, когда вы добавляете больше элементов. –

+0

@Elias Mårtenson: Append не копирует последний список. –

1

Иногда вы можете найти себе необходимость изменить список на месте, а не делать копии, в этом случае это может быть удобно:

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

Как сказал Райнер Йосвиг, вы должны использовать общую встроенную функцию lisp butlast.

Но, если вы все еще хотите увидеть, что эффективный рекурсивный вариант будет выглядеть следующим образом:

(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()))) 

Пока ваш лепет реализация поддерживает оптимизацию хвостового вызова, это будет конвертировано в петлю. Фокус в том, что всякий раз, когда вызывается butlast2-worker, его результат будет возвращен напрямую, что означает, что вам не нужно отслеживать аргументы/внутренние переменные из предыдущих вызовов функции. Последнее означает, что вам не нужно заполнять стек вызовов, как обычно, для рекурсивной функции.

Глядя на определение Райнера Йосвига, вы можете увидеть огромную разницу в размере. Вот сила loop, и научитесь использовать ее с умом, когда сможете (или лучше: используйте iteratehttp://common-lisp.net/project/iterate/).

8

Короткие и простые, как и Лисп. Вот волшебный материал:

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

0

насчет:

(defun butlast2 (L) 
    (if (null (rest L)) 
    nil 
    (cons (first L) (butlast2 (rest L))) 
) 
) 
Смежные вопросы