As I said in a comment: if you don't know how many values you want, then you probably should not be using multiple values: since the limit can be as low as 20 you may hit that limit, and any receiver will need to convert the values into a data structure to know how many there are. Simply returning the data structure is going to be better.
So I will assume that you do know how many values you want. In that case you can just bind a variable for each value. But no-one wants to write code like that, of course. Well, this is Lisp: you don't have to write boring repetitive code!
(defmacro with-indexed-values ((accessor n &optional (initial-value 'nil))
&body decls/forms)
"Evaluate DECLS/FORMS with N indexed values: (ACCESSOR i) will
retrieve the ith value, and (SETF (ACCESSOR I) v) will set it Indices
run from 0. All of the indexed values are returned as multiple
values. INITIAL-VALUE, if given is the initial value for the values:
it is evaluated once."
(unless (<= n multiple-values-limit)
(error "~A is too many values: limit is ~A" n multiple-values-limit))
(let ((varnames (loop for i below n
collect (make-symbol (format nil "V~D" i))))
(ivname (make-symbol "IV")))
`(let ((,ivname ,initial-value))
(let ,(loop for v in varnames collect `(,v ,ivname))
(flet ((,accessor (i)
(case i
,@(loop for v in varnames
for i upfrom 0
collect `(,i ,v))
(otherwise
(error "~A isnt an integer in [0, ~A)" i ,n))))
((setf ,accessor) (new i)
(case i
,@(loop for v in varnames
for i upfrom 0
collect `(,i (setf ,v new)))
(otherwise
(error "~A isnt an integer in [0, ~A)" i ,n)))))
(declare (inline ,accessor (setf ,accessor)))
,@decls/forms
(values ,@varnames))))))
And now:
> (with-indexed-values (v 2)
(setf (v 0) 2)
(setf (v 1) (+ (v 0) 1))
(decf (v 0)))
1
3
Or
> (with-indexed-values (v 5 (cons nil nil))
(loop for i below 3
do (setf (v i) i)))
0
1
2
(nil)
(nil)
(And the two last values are eq.)
Here is another version of the same macro: this one stashes the values in a vector, and relies on the vector being stack-allocated. LispWorks can do this: I have not checked other implementations. This one has the advantage that the accessors are simply array references and should be fast, but the disadvantage that it relies on the implementation stack-allocating the vector.
(defmacro with-indexed-values ((accessor n &optional (initial-value 'nil))
&body decls/forms)
"Evaluate DECLS/FORMS with N indexed values: (ACCESSOR i) will
retrieve the ith value, and (SETF (ACCESSOR I) v) will set it Indices
run from 0. All of the indexed values are returned as multiple
values. INITIAL-VALUE, if given is the initial value for the values:
it is evaluated once."
(unless (<= n multiple-values-limit)
(error "~A is too many values: limit is ~A" n multiple-values-limit))
(let ((vname (make-symbol "V")))
`(let ((,vname
(let ((init ,initial-value))
(vector ,@(loop repeat n collect 'init)))))
(declare (dynamic-extent ,vname))
(flet ((,accessor (i)
(aref ,vname i))
((setf ,accessor) (new i)
(setf (aref ,vname i) new)))
(declare (inline ,accessor (setf ,accessor)))
,@decls/forms
(values ,@(loop for i below n
collect `(aref ,vname ,i)))))))