2

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.

Mark Karpov
  • 7,499
  • 2
  • 27
  • 62

1 Answers1

7

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)

Community
  • 1
  • 1
sds
  • 58,617
  • 29
  • 161
  • 278
  • With which function do I copy an adjustable string with fill-pointer, so that result would be also adjustable string? I just tried your method with `copy-seq`, but it produces arrays with fixed-size... – Mark Karpov Jul 23 '14 at 14:27
  • 1
    @Mark: I don't think there is function which does just that, but you can whip one up yourself using `make-array` - or just ask a separate question. – sds Jul 23 '14 at 14:32
  • OK, thanks, now I know how to finish my algorithm. It's gonna be a bit bulky, but now I know that there is nothing better. – Mark Karpov Jul 23 '14 at 14:34