I was having trouble understanding the code for neighbors on page 314, so I worked up an alternative implementation based on a columns and row view:
(defun neighbors (pos)
(let* ((s *board-size*) (r (truncate pos s)) (c (mod pos s))
(r+ (1+ r)) (r- (1- r)) (c+ (1+ c)) (c- (1- c))
(candidate-points (list (cons r- c-) (cons r- c)
(cons r c-) (cons r c+)
(cons r+ c) (cons r+ c+))))
(labels ((on-grid-as-pos (point)
(let ((row (car point))
(col (cdr point)))
(and (>= col 0) (< col s) (>= row 0) (< row s)
(list (+ col (* row s)))))))
(mapcan #'on-grid-as-pos candidate-points)))))
I have two language questions. First, I disliked having to use both truncate and mod to convert position to row and column, given that truncate returns both of these values. However, I was unable to figure out how to a capture the second result within a let. Any ideas? Second, I dislike having to make each value returned by on-grid-as-pos be a list so I can use mapcan. Is there another, map--- function that when applied to a list, evaluates to a list with only some of the (transformed) elements?
I'm also open to feed back on how 'lispy' this alternate implementation is. As I say, I wrote it to be more understandable, although now that I understand the problem of finding neighbors, I find the book's implementation perfectly clear. That version is about 25% faster, mostly because this version creates lots of lexical variables and thus causes more GC.
Tom