Common lisp 从循环列表中删除自引用

Common lisp 从循环列表中删除自引用,common-lisp,circular-reference,circular-list,Common Lisp,Circular Reference,Circular List,我在Common Lisp中有一个复杂的循环数据结构: (defvar xs '#1=(1 #2=(#1# 2 #3=(#2# 3 #4=(#3# 4 #1#))))) 如何将其转换为非循环列表,以便将每次出现的自引用替换为nil?因此,我没有使用(1(#0 2(#1 3(#2 4#0)),而是使用(1(nil 2(nil 3(nil 4 nil)))?递归地迭代列表及其子列表,记住您以前遇到过哪些子列表: (defun remove-circles (list) (let ((seen

我在Common Lisp中有一个复杂的循环数据结构:

(defvar xs '#1=(1 #2=(#1# 2 #3=(#2# 3 #4=(#3# 4 #1#)))))

如何将其转换为非循环列表,以便将每次出现的自引用替换为
nil
?因此,我没有使用
(1(#0 2(#1 3(#2 4#0))
,而是使用
(1(nil 2(nil 3(nil 4 nil)))

递归地迭代列表及其子列表,记住您以前遇到过哪些子列表:

(defun remove-circles (list)
  (let ((seen (make-hash-table :test 'eq)))
    (labels ((rec (datum)
               (cond ((not (listp datum))
                      datum)
                     ((gethash datum seen)
                      nil)
                     (t
                      (setf (gethash datum seen) t)
                      (mapcar #'rec datum)))))
      (rec list))))

* (defvar xs '#1=(1 #2=(#1# 2 #3=(#2# 3 #4=(#3# 4 #1#)))))
XS
* xs
#1=(1 #2=(#1# 2 #3=(#2# 3 (#3# 4 #1#))))
* (remove-circles xs)
(1 (NIL 2 (NIL 3 (NIL 4 NIL))))

这将创建一个新列表-原始结构不会被修改。

最简单的方法是了解使用哈希表遇到的所有
cons
。即使循环发生在
cdr
中,此版本也可以工作:

(取消删除参考(列表和可选(值为零))
(let((h(生成哈希表:test#'eq)))
(标签)(rraux(列表)
(cond((gethash列表h)值)
((非(消费清单))清单)
(t(setf(gethash列表h)t)
(cons(rraux(车辆清单))
(rraux(cdr list(()())))
(rraux列表)
(删除参考号“#1=(12#2=(3 4 5.#1#)6 7.#1#)测试)
; ==> (1.2(3.4.5.测试)6.7.测试)
(删除参考号“#1=(12#2=(3 4 5.#1#)6 7.#1#))
; ==> (1 2 (3 4 5) 6 7)