Я пытаюсь написать функцию, которая разрушительно удалит N
элементов из списка и вернет их. Код, который я придумал (см. Ниже), выглядит отлично, за исключением того, что SETF
работает не так, как я предполагал.DELETE + SETF внутри функции
(defun pick (n from)
"Deletes (destructively) n random items from FROM list and returns them"
(loop with removed = nil
for i below (min n (length from)) do
(let ((to-delete (alexandria:random-elt from)))
(setf from (delete to-delete from :count 1 :test #'equal)
removed (nconc removed (list to-delete))))
finally (return removed)))
В большинстве случаев это работает просто отлично:
CL-USER> (defparameter foo (loop for i below 10 collect i))
CL-USER> (pick 3 foo)
(1 3 6)
CL-USER> foo
(0 2 4 5 7 8 9)
CL-USER> (pick 3 foo)
(8 7 0)
CL-USER> foo
(0 2 4 5 9)
Как вы можете видеть, PICK
работает просто отлично (на SBCL), если элемент быть выбранным не случается, первым в списке. В этом случае он не удаляется. Это потому, что единственное повторное назначение - это то, что происходит внутри DELETE
. SETF
не работает должным образом (т. Е. Если я использую REMOVE
, то FOO
не изменяется вообще).
Существует ли какое-либо правило определения, о котором я не знаю?
Итак, я только что нашел [это другой вопрос] (http://stackoverflow.com/questions/20074462/setf-in-a-function-does-not-work?rq=1), в котором похоже, аналогичная проблема. Однако я не использую цитированные данные. Здесь что-то не хватает? – Guilherme
'ниже (min n (length from))' isnt 'будет иметь большой смысл, если вы измените длину списка. –
Вы не можете написать функцию, которая * «Удаляет (разрушает) n случайных элементов из списка FROM и возвращает их» *. Что произойдет, если вы хотите удалить один случайный элемент из '(42)'. Вы не можете превратить ячейку cons в пустой список. –