普通 lisp 中意外循环列表的问题
Trouble with unintended circular lists in common lisp
运行 linux 中的 sbcl 1.3.7,我有一个对象,它有一个槽,它是一个循环列表,遵循 Rainer Joswig 在 Circular list in Common Lisp 中的建议和一个全局变量这是一个适当的列表(不是循环的)。
(setf *print-circle* t)
(defun circular (items)
(setf (cdr (last items)) items)
items)
(defclass circular ()
((items :accessor items :initarg :items)))
(defmethod initialize-instance :after ((c circular) &rest initargs)
(setf (slot-value c 'items)
(circular (slot-value c 'items))))
(defmethod next-item ((c circular))
(prog1 (first (slot-value c 'items))
(setf (slot-value c 'items)
(rest (slot-value c 'items)))))
(defparameter *notes* (make-instance 'circular :items '(A A# B C C# D D# E F F# G G#)))
(defparameter *stuff1* '(A A# B C C# D D# E F F# G G#))
(defparameter *stuff2* (list 'A 'A# 'B 'C 'C# 'D 'D# 'E 'F 'F# 'G 'G#))
我的问题是参数 *stuff1* 应该是一个简单的符号列表。或者我是这么想的。在 sbcl 中编译以上内容,*stuff1* returns
> *stuff1*
#1=(A |A#| B C |C#| D |D#| E F |F#| G |G#| . #1#)
我绝对没想到这个非循环列表会变成一个Sharpsign Equal-Sign项目。更重要的是,即使我 (setf *print-circle* t) 以下内容在 sbcl 没有错误的情况下挂起:
(member '|Bb| *stuff1*)
另一方面,*stuff2* 按预期工作。
所以,有两个问题,(1) 为什么 *stuff1* 列表变成了循环缺点,导致列表不正确,而 *stuff2* 仍然是正确的列表,以及 (2) 我如何测试成员资格*stuff1* 变成了?
显然我可以使用 *stuff2* 版本,但我显然误解了这里的一些关键内容。感谢任何指点。
文字列表
'(a b c)
是代码中引用的 文字列表 。 Common Lisp 标准说改变这些的效果是 undefined.
规则:不要更改文字列表。将它们视为常量数据。请参阅 list
、copy-list
和 copy-tree
等函数,了解如何在运行时创建全新的列表。
在代码中共享文字对象
代码中的两个文字列表,如 (a b c)
和 (a b c)
可以被编译器检测为相等。因为它们是常量数据(见上文),编译器可以生成代码,这样它只分配一个列表并且这个列表在多个地方共享。 SBCL 文件编译器会执行此操作。
规则:聪明的编译器可以共享相同的文字列表。
循环MEMBER
Common Lisp 的 member
仅支持 适当的列表 。不是 循环列表。
循环列表的 mapc
函数是一个很好的构建块:
(defun circular-mapc (function list)
(loop for fast = list then (cddr fast)
for slow = list then (cdr slow)
do
(funcall function (car slow))
(when (eq (cddr fast) (cdr slow))
(return-from circular-mapc nil))))
(defun circular (items)
(check-type items cons)
(setf (cdr (last items)) items)
items)
(defun circular-member-p (item list)
(circular-mapc (lambda (e)
(when (eql item e)
(return-from circular-member-p t)))
list))
示例:
CL-USER 38 > (circular-member-p 'a (circular (list 'a 'b 'c 'd 'e)))
T
CL-USER 39 > (circular-member-p 'b (circular (list 'a 'b 'c 'd 'e)))
T
CL-USER 40 > (circular-member-p 'c (circular (list 'a 'b 'c 'd 'e)))
T
CL-USER 41 > (circular-member-p 'd (circular (list 'a 'b 'c 'd 'e)))
T
CL-USER 42 > (circular-member-p 'e (circular (list 'a 'b 'c 'd 'e)))
T
CL-USER 43 > (circular-member-p 'f (circular (list 'a 'b 'c 'd 'e)))
NIL
运行 linux 中的 sbcl 1.3.7,我有一个对象,它有一个槽,它是一个循环列表,遵循 Rainer Joswig 在 Circular list in Common Lisp 中的建议和一个全局变量这是一个适当的列表(不是循环的)。
(setf *print-circle* t)
(defun circular (items)
(setf (cdr (last items)) items)
items)
(defclass circular ()
((items :accessor items :initarg :items)))
(defmethod initialize-instance :after ((c circular) &rest initargs)
(setf (slot-value c 'items)
(circular (slot-value c 'items))))
(defmethod next-item ((c circular))
(prog1 (first (slot-value c 'items))
(setf (slot-value c 'items)
(rest (slot-value c 'items)))))
(defparameter *notes* (make-instance 'circular :items '(A A# B C C# D D# E F F# G G#)))
(defparameter *stuff1* '(A A# B C C# D D# E F F# G G#))
(defparameter *stuff2* (list 'A 'A# 'B 'C 'C# 'D 'D# 'E 'F 'F# 'G 'G#))
我的问题是参数 *stuff1* 应该是一个简单的符号列表。或者我是这么想的。在 sbcl 中编译以上内容,*stuff1* returns
> *stuff1*
#1=(A |A#| B C |C#| D |D#| E F |F#| G |G#| . #1#)
我绝对没想到这个非循环列表会变成一个Sharpsign Equal-Sign项目。更重要的是,即使我 (setf *print-circle* t) 以下内容在 sbcl 没有错误的情况下挂起:
(member '|Bb| *stuff1*)
另一方面,*stuff2* 按预期工作。
所以,有两个问题,(1) 为什么 *stuff1* 列表变成了循环缺点,导致列表不正确,而 *stuff2* 仍然是正确的列表,以及 (2) 我如何测试成员资格*stuff1* 变成了?
显然我可以使用 *stuff2* 版本,但我显然误解了这里的一些关键内容。感谢任何指点。
文字列表
'(a b c)
是代码中引用的 文字列表 。 Common Lisp 标准说改变这些的效果是 undefined.
规则:不要更改文字列表。将它们视为常量数据。请参阅 list
、copy-list
和 copy-tree
等函数,了解如何在运行时创建全新的列表。
在代码中共享文字对象
代码中的两个文字列表,如 (a b c)
和 (a b c)
可以被编译器检测为相等。因为它们是常量数据(见上文),编译器可以生成代码,这样它只分配一个列表并且这个列表在多个地方共享。 SBCL 文件编译器会执行此操作。
规则:聪明的编译器可以共享相同的文字列表。
循环MEMBER
Common Lisp 的 member
仅支持 适当的列表 。不是 循环列表。
循环列表的 mapc
函数是一个很好的构建块:
(defun circular-mapc (function list)
(loop for fast = list then (cddr fast)
for slow = list then (cdr slow)
do
(funcall function (car slow))
(when (eq (cddr fast) (cdr slow))
(return-from circular-mapc nil))))
(defun circular (items)
(check-type items cons)
(setf (cdr (last items)) items)
items)
(defun circular-member-p (item list)
(circular-mapc (lambda (e)
(when (eql item e)
(return-from circular-member-p t)))
list))
示例:
CL-USER 38 > (circular-member-p 'a (circular (list 'a 'b 'c 'd 'e)))
T
CL-USER 39 > (circular-member-p 'b (circular (list 'a 'b 'c 'd 'e)))
T
CL-USER 40 > (circular-member-p 'c (circular (list 'a 'b 'c 'd 'e)))
T
CL-USER 41 > (circular-member-p 'd (circular (list 'a 'b 'c 'd 'e)))
T
CL-USER 42 > (circular-member-p 'e (circular (list 'a 'b 'c 'd 'e)))
T
CL-USER 43 > (circular-member-p 'f (circular (list 'a 'b 'c 'd 'e)))
NIL