SETF не завершает работу и не сообщает об ошибке

Я новичок в Common Lisp и наткнулся на этот фрагмент кода:

(let ((foo (list 42)))
  (setf (rest foo) foo))

REPL, кажется, просто зацикливается при попытке выполнить его.


person hayordi    schedule 01.12.2016    source источник


Ответы (1)


Что такое FOO?

FOO изначально свежий список, (42). В Лиспе списки представлены ячейками cons, блоками изменяемой памяти, содержащими CAR и CDR слот. Другой способ печати — (42 . NIL), где CAR и CDR находятся с каждой стороны точки. Это также можно изобразить следующим образом:

  car  cdr
------------
| 42 | NIL |
------------
     ^
     |
    FOO

Когда вы вызываете SETF с (rest foo) место и foo значение, вы говорите, что хотите, чтобы ячейка cdr FOO содержала значение FOO. Фактически, это вхождение SETF, скорее всего, макрорасширится в вызов RPLACD.

------------
| 42 | FOO |
------------
     ^
     |
    FOO

Почему REPL зацикливается навсегда?

Часть «P» «REPL» (печать) пытается напечатать вашу круговую структуру. Это связано с тем, что значение SETF возвращается из оцениваемой формы, а значение, возвращаемое SETF, является значением его второго аргумента, а именно FOO. Представьте, что вы хотите написать cons-ячейку X с наивным алгоритмом:

1. PRINT "("
2. PRINT the CAR of X
3. PRINT " . "
4. PRINT the CDR of X
5. PRINT ")"

Однако для foo на шаге 4 будет напечатана та же самая структура, рекурсивно, и она никогда не завершится.

Что ты можешь сделать?

Попробуйте сначала установить для *PRINT-CIRCLE* значение T:

(setf *print-circle* t)

И теперь ваш REPL должен быть счастлив:

CL-USER> (let ((foo (list 42)))
           (setf (rest foo) foo))
#1=(42 . #1#)

Нотация "Sharpsign Equal-Sign" позволяет читателю воздействовать на часть формы, чтобы переменная (читатель), например #1=..., и повторно использовать ее впоследствии, например. #1#. Это позволяет представлять циклические перекрестные ссылки между данными во время чтения или печати. Здесь мы видим, что переменная #1# обозначает cons-ячейку, где CAR — это 42, а CDR — это #1#.

person coredump    schedule 02.12.2016
comment
красивый ответ! - person Gavin Lock; 02.12.2016