Mark Karpov
Mark Karpov

Reputation: 7599

Repeating elements of list with copying

I need to repeat every element of a list N times, i.e. perform this kind of transformation:

(1 2 3) => (1 1 1 2 2 2 3 3 3) ; N = 3

It is important to preserve the order of elements, i.e. first element should be repeated N times, then second, etc.

This is my best attempt so far:

(defun my-fnc (lst &optional (n 2))
  (mapcan (lambda (x) (make-list n :initial-element x))
          lst))

Looks like it works:

CL-USER> (defparameter *foo* '("foo" "bar"))
*FOO*
CL-USER> (setf *foo* (my-fnc *foo* 3))
("foo" "foo" "foo" "bar" "bar" "bar")

...but not quite. The problem is that former three elements are references to the same object.

("foo" "foo" "foo" "bar" "bar" "bar")
;{---------------} {---------------}
; the same string   the same string

This is not what I want.

So my question is: how to solve the problem in most idiomatic way, so that every element of result list would be reference to copied separate object.

Upvotes: 2

Views: 964

Answers (1)

sds
sds

Reputation: 60074

This is impossible to do in general because Common Lisp does not provide a generic copy function. Moreover,

  • some objects are immediate (e.g., fixnum) and cannot be copied in any meaningful sense
  • some objects are immutable and copying them is a waste
  • some objects are nested and you will have to decide whether you want deep or shallow copy

However, if you have resolved the problem and provided the copy function, it is not too hard:

(defun my-fnc (list &key (repeat 2) copy-function)
  (mapcan (if copy-function
              (lambda (x) 
                (loop :repeat repeat :collect (funcall copy-function x)))
              (lambda (x) (make-list n :initial-element x)))
          list)) 

Note that all the element of the list argument are copied, i.e., the return value has no intersection with the argument (under the eq test and assuming that the copy-function returns a fresh object)

Upvotes: 7

Related Questions