First, some reformatting:
(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))))
Then, if you don't need an alternative if clause, but want progn , you can use 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))))
The last two if clauses are mutually exclusive, so cond or case is suitable (I will use 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))))
There is a zerop predicate:
(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))))
I do not see that 0 should be added to some value:
(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))))
If you are not sure if the pick parameter is set to a reasonable default, you should have a default case (this may be one of your problems):
(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))
Instead of using setq and nconc you can use push (this puts a new item at the top of the list, but since you randomly select randomly, this should not be a concern):
(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))
I suspect that at the beginning of this fragment choices should be () , that you do not need choices after this fragment, and the choices print is for debugging only, so you could do it differently using remove-if and changing the condition:
(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)))
If choices now prints as () , that means there is no choice, so you will have to digress (or whatever your algorithm does when the deadlock is reached).
Finally, since (length choices) can only be a non-negative integer, you can use case instead of cond if you check cases in a different order:
(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)))))
Update on request.
As Rainer points out, this is basically the body of the pick function, so we can get rid of all the free variables. Alternatively, instead of car you can use (for lists) the more descriptive name 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)))))
This function will be defined elsewhere, and at the fragment location it will be called like this:
(pick entries sub col)
In order not to double (length choices) , we can put this in let (which needs to become let* for consistent evaluation):
(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)))))
The last step (truly optional, but you might find that you have more sequences that reduce your options, like row ) will be a small generalization:
(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)))))
The call to this function has the same form, but now you can use any number of exception sequences:
(pick entries col sub row ver ima fou)