I am not sure why using REVERSE not effective enough; did you really profile the solution? You just go through the list once to do this; same as (let's say) find the length of a list, and then you can go through two lists once for comparison.
If you would like to be a little favorite, you could simultaneously find the length of the list and cancel it with LOOP as follows:
(defun fancier-palindrome-p (list) (let ((reversed '()) (length 0)) (dolist (elt list) (incf length) (push elt reversed)) (dotimes (i (floor length 2) t) (unless (eql (pop list) (pop reversed)) (return nil)))))
This allows you to skip half of the checks. I do not think it is worth the extra complexity. You can also use the move down the list with a turtle and a hare to save half the cost due to even greater complexity.
(defun ridiculous-palindrome-p (list) (let ((reversed-front '())) (loop :for tortoise :on list :for hare :on list :by #'cddr :until (null (cdr hare)) :do (push (car tortoise) reversed-front) :finally (return (if (null hare) ; length is even (equal tortoise reversed-front) (equal (cdr tortoise) reversed-front))))))
None of these decisions seem more convincing to me than
(defun palindrome-p (list) (equal list (reverse list))
If this is really a bottleneck, you might be better off using vectors as a sequence representation to take advantage of fast random access, for example:
(defun vector-palindrome-p (vector) (let* ((n (length vector)) (jn)) (dotimes (i (floor n 2) (return t)) (unless (eql (aref vector i) (aref vector (decf j))) (return nil)))))
source share