为什么这个mapcan会导致我的REPL冻结?

时间:2013-11-26 01:41:50

标签: lisp common-lisp

this very useful answer中,有人建议我可以替换此代码:

(defun describe-paths (location edges)
  (apply (function append) (mapcar #'describe-path
               (cdr (assoc location edges)))))

有了这个:

(defun describe-paths-mapcan (location edges)
  (mapcan #'describe-path
               (cdr (assoc location edges))))

我当然从概念上理解为什么这应该有用,但事实并非如此;第二个变体冻结我的REPL,CL提示永远不会返回。我必须重启SLIME。所以我looked it up,我想知道mapcan不使用list而不是nconc的事实是什么原因?因此,这些实际上不是功能相同的代码块吗?

对于好奇,我传递了这个:

(describe-paths-mapcan 'living-room *edges*)

*edges*的位置:

(defparameter *edges* '((living-room (garden west door)
             (attic upstairs ladder))
            (garden (living-room east door))
            (attic (living-room downstairs ladder))))

(defun describe-path (edge)
  `(there is a ,(caddr edge) going ,(cadr edge) from here.))

1 个答案:

答案 0 :(得分:7)

我认为这与describe-edges有关。它被定义为:

(defun describe-path (edge)
  `(there is a ,(caddr edge) going ,(cadr edge) from here.))

那里的quasiquote我们可以macroexpand ..你得到:

(macroexpand '`(there is a ,(caddr edge) going ,(cadr edge) from here.)) ; ==>
(CONS 'THERE
 (CONS 'IS
  (CONS 'A
   (CONS (CADDR EDGE) (CONS 'GOING (CONS (CADR EDGE) '(FROM HERE.)))))))

根据documentation for mapcan,破坏性的破坏是完成的。查看从describe-path返回的最后一个元素将与它返回的下一个元素共享结构,因此nconc将进行无限循环。

如果您要将describe-edges更改为以下内容,则可以使用

(defun describe-path (edge)
  (list 'there 'is 'a (caddr edge) 'going (cadr edge) 'from 'here.))