Search code examples
listinsertschemecircular-list

Insert element to circular list using scheme


I have a circular list, eg: #0=(1 2 3 4 . #0#).

What I want to do is to insert a new element (x) into this list so that the outcome is #0=(x 1 2 3 4 . #0#). I have been trying using this code (x is the circular list):

(define (insert! elm)
  (let ((temp x))
    (set-car! x elm)
    (set-cdr! x temp)))

However, I think that set-cdr! is not working like I want it to. What am I missing here? Maybe I am way off?


Solution

  • The easiest way to prepend an element to a list is to modify the car of the list, and set the cdr of the list to a new cons whose car is the original first element of the list and whose cdr is the original tail of the list:

    (define (prepend! x list)                      ; list = (a . (b ...)) 
      (set-cdr! list (cons (car list) (cdr list))) ; list = (a . (a . (b ...)))
      (set-car! list x))                           ; list = (x . (a . (b ...)))
    

    (let ((l (list 1 2 3)))
      (prepend! 'x l)
      (display l))
    ;=> (x 1 2 3)
    

    Now, that will still work with circular lists, because the cons cell (i.e., pair) that is the beginning of the list remains the same, so the "final" cdr will still point back to object that is the beginning. To test this, though, we need some functions to create and sample from circular lists, since they're not included in the language (as far as I know).

    (define (make-circular list)
      (let loop ((tail list))
        (cond
          ((null? (cdr tail))
           (set-cdr! tail list)
           list)
          (else
           (loop (cdr tail))))))
    
    (define (take n list)
      (if (= n 0)
          '()
          (cons (car list)
                (take (- n 1)
                      (cdr list)))))
    

    (display (take 10 (make-circular (list 1 2 3))))
    ;=> (1 2 3 1 2 3 1 2 3 1)
    

    Now we can check what happens if we prepend to a circular list:

    (let ((l (make-circular (list 1 2 3))))
      (prepend! 'x l)
      (display (take 15 l)))
    ;=> (x 1 2 3 x 1 2 3 x 1 2 3 x 1 2)