常见的lisp中的意外循环列表有问题

时间:2016-09-04 04:41:49

标签: common-lisp circular-list

在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 *返回

> *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 *版本,但我显然误解了一些关键的东西。任何指针都表示赞赏。

1 个答案:

答案 0 :(得分:8)

文字列表

'(a b c)是代码中引用的文字列表。 Common Lisp标准说改变它们的效果是 undefined

规则:不要改变文字列表。将它们视为常量数据。请参阅listcopy-listcopy-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