在Common Lisp中的循环链表

11

我正在使用一个基于CL的音乐作曲可视化编程环境。我尝试创建一个函数,当给定3个元素(1 2 3)时,将返回1,2,3,1,2,3等,每次评估时逐个输出序列中的数字。书籍《Common Lisp a Gentle Introduction》简要提到可以使用“#=”符号表示法创建循环列表,但没有详细介绍如何使用它们。 请注意,我可以使用专门设计的对象在程序中插入实际的Lisp代码。


4个回答

19
CL-USER 3 > (defun circular (items)
              (setf (cdr (last items)) items)
              items)
CIRCULAR

CL-USER 4 > (setf *print-circle* t)
T

CL-USER 5 > (circular (list 1 2 3))
#1=(1 2 3 . #1#)

例子:

CL-USER 16 > (setf c1 (circular (list 1 2 3)))
#1=(1 2 3 . #1#)

CL-USER 17 > (pop c1)
1

CL-USER 18 > (pop c1)
2

CL-USER 19 > (pop c1)
3

CL-USER 20 > (pop c1)
1

另外:

CL-USER 6 > '#1=(1 2 3 . #1#)
#1=(1 2 3 . #1#)

加入一些CLOS:

(defclass circular ()
  ((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)))))

CL-USER 7 > (setf circ1 (make-instance 'circular :items (list 1 2 3)))
#<CIRCULAR 40200017CB>

CL-USER 8 > (next-item circ1)
1

CL-USER 9 > (next-item circ1)
2

CL-USER 10 > (next-item circ1)
3

CL-USER 11 > (next-item circ1)
1

CL-USER 12 > (next-item circ1)
2

你有没有推荐的书籍或网站可以帮助我更多地了解这方面的知识? - Federico Bonacossa
1
《Let Over Lambda》一书中有关于循环表达式的章节:http://letoverlambda.com/index.cl/guest/chap4.html#sec_5 - Clayton Stanley
circular的定义中不需要返回items,因为setf会自动将其返回。 - anonymous
3
@tsikov: 是的,但我希望原文更加清晰明了,不要依赖读者的知识水平。 - Rainer Joswig

12
Sharpsign Equal-Sign记法中,它被写成#0=(1 2 3 . #0#)
这是一个从给定参数创建这样一个列表的函数:
(defun circular (first &rest rest)
  (let ((items (cons first rest)))
    (setf (cdr (last items)) items)))

然后,调用(circular 1 2 3)将返回您想要的循环列表。只需使用carcdr无限迭代元素。

如果您真的想要一个不带参数并在每次调用时返回下一个项目的迭代器函数,则可以通过以下方式实现:

(defun make-iter (list)
  (lambda ()
    (pop list)))

1
你可以使用PROG1代替LET。 - Thomas Bartscher
2
@ThomasBartscher 谢谢!现在已经实现了。(实际上我看了Rainer的回答,似乎pop做的就是我想要的事情。这就是当一个Schemer试图写CL时会发生的事情。;-)) - C. K. Young
1
哦,我没有想到那个。不错! - Thomas Bartscher

2

首先,您需要让打印机知道要识别循环列表而不是尝试打印整个列表:

(setf *print-circle* t)

接下来,你可以使用“#=”符号创建一个循环列表:

(setq x '#1=(1 2 3 . #1#))


0

这里是一个在Lisp中实现循环列表的想法。

;;; Showing structure of the list
;;; (next prev is-end val)

; create items
setf L-0 (L-1 L-3 t "L-0 sentry")  ; this will be the sentry item so know where to stop
setf L-1 (L-2 L-0 nil "L-1")
setf L-2 (L-3 L-1 nil "L-2")
setf L-3 (L-0 L-2 nil "L-3")

; how to access L-2 from L-0
eval (first (eval (first L-0)))

; result: (L-3 L-1 NIL "L-2")

我不会提供defun函数来添加、删除和访问项目。我认为我提供的已经足够展示你在定义这种循环列表的任何函数中需要做什么。在Listener中,这对我来说似乎是有效的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接