2014-11-17 3 views
5

Я развернул макрос ниже, чтобы увидеть, как он работает, и нашел себя немного смущенным.Понимание расширения макросов цикла

(loop for i below 4 collect i) 

расширяется (я прибрал ее немного для удобства чтения)

(block nil 
    (let ((i 0)) 
    (declare (type (and number real) i)) 
    (let* ((list-head (list nil)) 
      (list-tail list-head)) 
     (tagbody 
     sb-loop::next-loop 
     (when (>= i 4) (go sb-loop::end-loop)) 
     (rplacd list-tail (setq list-tail (list i))) 
     (setq i (1+ i)) 

     (print "-------") ;; added so I could see the lists grow 
     (print list-head) 
     (print list-tail) 
     (print "-------") 

     (go sb-loop::next-loop) 
     sb-loop::end-loop 
     (return-from nil (cdr list-head)))))) 

..и здесь есть выход из работы выше ..

;; "-------" 
;; (NIL 0) 
;; (0) 
;; "-------" 
;; "-------" 
;; (NIL 0 1) 
;; (1) 
;; "-------" 
;; "-------" 
;; (NIL 0 1 2) 
;; (2) 
;; "-------" 
;; "-------" 
;; (NIL 0 1 2 3) 
;; (3) 
;; "-------" 

Я просто не могу 't видеть, где изменен список, я должен предположить, что голова и хвост - это eq, и, таким образом, изменение одного из них изменило другое, но кто-то может сломать то, что происходит на линии rplacd е?

+1

Почему вы хотите изменить список? См. Это как один связанный список: голова не изменяется, только хвост, когда вы добавляете новый элемент. Тем не менее, функция 'print', применяемая в списке, печатает список из текущего элемента в последний элемент, поэтому вы видите рост списка и изменение списка. – Bentoy13

+1

Я не хочу, чтобы это было изменено. Я хочу понять, что на самом деле происходит. Если вывод на печать затрудняет ситуацию, возможно, поэтому я не понимаю. Вы должны оставить свой комментарий в ответе, чтобы я мог послать вам карму! – Baggers

+2

+1 для хорошего вопроса, который врывается в исходный код, использует макрорасширение, показывает четкое поведение, о котором возникает вопрос и т. Д. Приятно! –

ответ

9

list-head и list-tail изначально same (в смысле eq). list-head - это минус, который cdr является списком. list-tail указывает на последние минусы в списке (кроме первоначально, см. Ниже).

Чтобы добавить элемент в конец списка, replacd изменяет cdr list-tail, чтобы добавить новые минусы, и list-tail обновлен, чтобы указать на новые минусы.

Когда цикл завершается, результатом является cdr list-head.

Почему этот сложный бизнес с дополнительными минусами? Поскольку алгоритм добавления списка упрощается, когда list-tail всегда является указателем на последние минусы списка. Но вначале пустой список не имеет недостатков. Таким образом, уловка состоит в том, чтобы сделать список длиннее.

+0

И все так же все имеет смысл! Спасибо, оказывается, я ввязывался в порядок оценки в моей голове. Это полностью очистило все. Ура! – Baggers

+0

Кредиты должны идти в Ричард С Уотерс. См. «Реализация очередей в Lisp» в «Некоторые полезные алгоритмы Lisp: Часть 1» http://www.merl.com/publications/docs/TR91-04.pdf (Также ознакомьтесь с «Часть 2».) –

Смежные вопросы