If you use the SERIES package, you can use the SERIES function which:
Creates an infinite series that endlessly repeats the given items in
the order given.
This works if the items are known statically, ie. not from a list of values given at runtime.
Here below, the code scans a list and, at the same time, iterates over the infinite serie 1 2 3 .... Both series are iterated in parallel with mapping, as s and n, and mapping generates a series of (cons s n). The result is collected as a list:
(collect 'list
(mapping ((s (scan 'list '(a b c d e f g h)))
(n (series 1 2 3)))
(cons s n)))
The result is:
((A . 1) (B . 2) (C . 3) (D . 1) (E . 2) (F . 3) (G . 1) (H . 2))
Series are subject to stream-fusion, and the expanded code is:
(let* ((#:out-823 (list 1 2 3)))
(let (#:elements-816
(#:listptr-817 '(a b c d e f g h))
#:items-821
(#:lst-822 (copy-list #:out-823))
#:items-826
(#:lastcons-813 (list nil))
#:lst-814)
(declare (type list #:listptr-817)
(type list #:lst-822)
(type cons #:lastcons-813)
(type list #:lst-814))
(setq #:lst-822 (nconc #:lst-822 #:lst-822))
(setq #:lst-814 #:lastcons-813)
(tagbody
#:ll-827
(if (endp #:listptr-817)
(go series::end))
(setq #:elements-816 (car #:listptr-817))
(setq #:listptr-817 (cdr #:listptr-817))
(setq #:items-821 (car #:lst-822))
(setq #:lst-822 (cdr #:lst-822))
(setq #:items-826 ((lambda (s n) (cons s n)) #:elements-816 #:items-821))
(setq #:lastcons-813 (setf (cdr #:lastcons-813) (cons #:items-826 nil)))
(go #:ll-827)
series::end)
(cdr #:lst-814)))
If you need to pass an arbitrary list to the function, you can make your own circular list by calling (nconc list list), which is simpler to write. Since you probably don't want to mutate the input list, you can write this instead:
(defun circularize (list)
(let ((list (copy-list list)))
(nconc list list)))