This is a spot where recursion shines.
You can treat the list as immutable, and creating a new list is fairly cheap since a traversal is required anyway.
So the caller gets the value and a new list, instead of destructively modifying the original list.
Unfortunately, this gets a little buried in the trappings to enable TCO and the syntax required for multiple values.
Code: Select all
(deftype non-negative-fixnum ()
`(integer 0 ,most-positive-fixnum))
(defun pick (index list)
(declare (non-negative-fixnum index) ; use a type check to guarantee that (>= index 0)
(optimize (speed 3))) ; enable TCO
(if (= index 0)
(values (car list) (cdr list))
(multiple-value-bind (x tail) (pick (1- index) (cdr list))
(values x (cons (car list) tail)))))
Here's an example use. Note that SETF on VALUES can be used instead of MULTIPLE-VALUE-BIND.
Code: Select all
(let ((x nil)
(l '(1 2 3 4 5)))
(setf (values x l) (pick 3 l))
(list x l))
Relevant links:
https://0branch.com/notes/tco-cl.html
https://common-lisp.net/project/cdr/doc ... types.html
http://wiki.c2.com/?PurelyFunctionalDataStructures
Edit:
The TCO isn't working for me. A simple "(pick 1000000 nil)" blows the stack. Probably because of the M-V-B.

I don't feel like fixing this. The solution would look like David's code, just in a recursive form, probably using an flet helper.