Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Circular list in Common Lisp

I am working using a visual programming environment for musical composition based on CL . I am trying to create a function that when given say 3 elements (1 2 3) will return 1, 2, 3, 1, 2, 3 etc., one number at the time each time it is evaluated. The book Common Lisp a Gentle Introduction, mentions briefly that it's possible to create circular lists using sharp-equal notation but does not get into details on how to use them. Keep in mind that I can insert actual Lisp code in the program using a object specifically designed for that.

like image 215
Federico Bonacossa Avatar asked May 21 '13 19:05

Federico Bonacossa


2 Answers

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#)

Example:

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

also:

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

With a bit of CLOS added:

(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
like image 81
Rainer Joswig Avatar answered Oct 25 '22 17:10

Rainer Joswig


In Sharpsign Equal-Sign notation, it's written as #0=(1 2 3 . #0#).

Here's a function which creates such a list from the given arguments:

(defun circular (first &rest rest)
  (let ((items (cons first rest)))
    (setf (cdr (last items)) items)))

Then, calling (circular 1 2 3) will return the circular list you wanted. Just use car and cdr to iterate through the elements ad infinitum.

And if you really want an iterator function that takes no arguments and returns the next item for each call, here's how you might do it:

(defun make-iter (list)
  (lambda ()
    (pop list)))
like image 20
Chris Jester-Young Avatar answered Oct 25 '22 17:10

Chris Jester-Young