В CHECK-FOR-WIN:
КОНД - плохой выбор для того, для чего он предназначен. Считать
об этом: вы хотите, чтобы функция возвращала T, если какая-либо из линий IS-LINE
вернуть T и NIL в противном случае. Ну, это в значительной степени определение
что ИЛИ делает, поэтому выгрузите COND и соберите вызовы IS-LINE в
один ИЛИ. Вы можете использовать НЕКОТОРЫЕ, чтобы сократить его еще больше, но это
может оказаться слишком «умным».
В IS-LINE
Давайте возьмем это наизнанку: во-первых, EQL является транзитивным, поэтому, если вы знаете,
(EQL A B) и (EQL A C), то для тестирования это избыточно (EQL B C).
Теперь, когда IF, абсолютно непростительно. Это буквально то же самое, что и
if (x)
return true;
else
return false;
на языке фигурных скобок. У вас уже есть значение истины, которое вы хотите
вернись, просто верни это.
Наконец, это плохой стиль для теневых переменных, как вы делаете с
LET. Во всяком случае, я бы сказал, что бросая один EQL, вы
в любом случае уменьшите необходимость предварительной обработки ссылок на массив почти до нуля.
В общем
Соглашение в Common Lisp для именования предикатов (функций, которые
вернуть либо T, либо NIL) это придумать существительную фразу, которая
описывает то, что они проверяют, и нажмите «р». Я так думаю
WINNING-POSITION-P и CELLS-MATCH-P будут более подходящими именами.
Я думаю, что было бы неплохо написать функцию для получения контента
квадратной доски в отличие от использования AREF, так как последний выставляет
подробности его реализации. Даже если это относительно незначительная проблема
в этом случае это хорошая привычка.
Следование этим советам приведет к следующему коду:
(defun winning-position-p ()
(or (cells-match-p 1 2 3)
(cells-match-p 1 4 7)
(cells-match-p 1 5 9)
(cells-match-p 2 5 8)
(cells-match-p 3 6 9)
(cells-match-p 3 5 7)
(cells-match-p 4 5 6)
(cells-match-p 7 8 9)))
(defun cells-match-p (a b c)
(and (eql (board-ref a)
(board-ref b))
(eql (board-ref a)
(board-ref c)))
(defun board-ref (cell)
;; Could check for errors here.
(aref *board* (1- cell)))