Сначала небольшое переформатирование:
(loop for e in entries do
(if (and (not (member e sub))
(not (member e col)))
(progn (setq choices (nconc choices (list e)))
(print choices)))
(if (= (length choices) 1)
(setq pick (car choices))
(if (not (= (length choices) 0))
(setq pick (nth (random (+ 0 (length choices))) choices))))
Тогда, если вам не нужно альтернативное предложение if
, но вы хотите progn
, вы можете использовать when
:
(loop for e in entries do
(when (and (not (member e sub))
(not (member e col)))
(setq choices (nconc choices (list e)))
(print choices))
(if (= (length choices) 1)
(setq pick (car choices))
(if (not (= (length choices) 0))
(setq pick (nth (random (+ 0 (length choices))) choices))))
Последние два предложения if
являются взаимоисключающими, поэтому подойдет либо cond
, либо case
(сейчас я буду использовать cond
):
(loop for e in entries do
(when (and (not (member e sub))
(not (member e col)))
(setq choices (nconc choices (list e)))
(print choices))
(cond ((= (length choices) 1)
(setq pick (car choices)))
((not (= (length choices) 0))
(setq pick (nth (random (+ 0 (length choices))) choices))))
Существует предикат zerop
:
(loop for e in entries do
(when (and (not (member e sub))
(not (member e col)))
(setq choices (nconc choices (list e)))
(print choices))
(cond ((= (length choices) 1)
(setq pick (car choices)))
((not (zerop (length choices)))
(setq pick (nth (random (+ 0 (length choices))) choices))))
Я не вижу, что должно достигнуть добавление 0 к некоторому значению:
(loop for e in entries do
(when (and (not (member e sub))
(not (member e col)))
(setq choices (nconc choices (list e)))
(print choices))
(cond ((= (length choices) 1)
(setq pick (car choices)))
((not (zerop (length choices)))
(setq pick (nth (random (length choices)) choices))))
Если вы не уверены, что для pick
задано разумное значение по умолчанию, возможно, у вас должен быть регистр по умолчанию (это может быть одной из ваших проблем):
(loop for e in entries do
(when (and (not (member e sub))
(not (member e col)))
(setq choices (nconc choices (list e)))
(print choices))
(cond ((= (length choices) 1)
(setq pick (car choices)))
((not (zerop (length choices)))
(setq pick (nth (random (length choices)) choices)))
(t
(setq pick nil))
Вместо использования setq
и nconc
вы можете использовать push
(это помещает новый элемент в начало списка, но так как вы выбираете случайным образом, это не должно вызывать беспокойства):
(loop for e in entries do
(when (and (not (member e sub))
(not (member e col)))
(push e choices)
(print choices))
(cond ((= (length choices) 1)
(setq pick (car choices)))
((not (zerop (length choices)))
(setq pick (nth (random (length choices)) choices)))
(t
(setq pick nil))
Я подозреваю, что в начале этого фрагмента choices
должен быть ()
, что вам не нужно choices
после этого фрагмента, и что печать choices
только для отладки, так что вы можно сделать это по-другому, используя remove-if
и изменив условие:
(let ((choices (remove-if (lambda (e)
(or (member e sub)
(member e col)))
entries)))
(print choices)
(cond ((= (length choices) 1)
(setq pick (car choices)))
((not (zerop (length choices)))
(setq pick (nth (random (length choices)) choices)))
(t
(setq pick nil)))
Если choices
теперь печатается как ()
, это означает, что здесь не осталось выбора, поэтому вам придется затем выполнить некоторый возврат (или то, что ваш алгоритм делает, когда достигнут тупик).
Наконец, поскольку (length choices)
могут быть только неотрицательными целыми числами, вы можете использовать case
вместо cond
, если вы проверяете случаи в другом порядке:
(let ((choices (remove-if (lambda (e)
(or (member e sub)
(member e col)))
entries)))
(print choices)
(case (length choices)
(0 (setq pick nil))
(1 (setq pick (car choices)))
(otherwise (setq pick (nth (random (length choices)) choices)))))
Обновление по запросу.
Как указывает Райнер, это в основном тело функции pick
, поэтому мы можем избавиться от всех свободных переменных. Кроме того, вместо car
вы можете использовать (для списков) более описательное имя first
:
(defun pick (entries sub col)
(let ((choices (remove-if (lambda (e)
(or (member e sub)
(member e col)))
entries)))
(print choices)
(case (length choices)
(0 nil)
(1 (first choices))
(otherwise (nth (random (length choices)) choices)))))
Эта функция будет определена в другом месте, а вместо фрагмента она будет вызываться так:
(pick entries sub col)
Чтобы не вычислять (length choices)
дважды, мы можем поместить это в let
(который должен стать let*
для последовательной оценки):
(defun pick (entries sub col)
(let* ((choices (remove-if (lambda (e)
(or (member e sub)
(member e col)))
entries))
(choices-length (length choices)))
(print choices)
(case choices-length
(0 nil)
(1 (first choices))
(otherwise (nth (random choices-length) choices)))))
Последний шаг (действительно необязательный, но, возможно, вы обнаружите, что у вас есть больше последовательностей, уменьшающих ваш выбор, например, row
), будет немного обобщением:
(defun pick (entries &rest exclusion-sequences)
(let* ((choices (remove-if (lambda (e)
(some #'identity
(mapcar (lambda (seq)
(member e seq))
exclusion-sequences)))
entries))
(choices-length (length choices)))
(print choices)
(case choices-length
(0 nil)
(1 (first choices))
(otherwise (nth (random choices-length) choices)))))
Вызов этой функции имеет ту же форму, но теперь вы можете использовать любое количество последовательностей исключения:
(pick entries col sub row ver ima fou)