让我们假设我有一个2d列表(如果你愿意,还有数组),如下所示:
'( (I I I O)
(I X I O)
(I I I O))
现在让我们假设我想找到X的所有邻居。在这种情况下,我的函数将返回8个I:s的列表。我将如何以智能方式实现此功能?我已经实现了一个看起来像这样的函数:
(defun get-neighbours (board x y)
(let ((output '() ))
(push (get-if-possible board (1+ x) y) output)
(push (get-if-possible board (1- x) y) output)
(push (get-if-possible board x (1+ y)) output)
(push (get-if-possible board x (1- y)) output)
(push (get-if-possible board (1+ x) (1+ y)) output)
(push (get-if-possible board (1- x) (1- y)) output)
(push (get-if-possible board (1+ x) (1- y)) output)
(push (get-if-possible board (1- x) (1+ y)) output)
output))
就是这样......丑陋。
答案 0 :(得分:4)
准备了这样的东西:
(defconstant +neighbour-offsets+
'((-1 . -1) (0 . -1) (1 . -1)
(-1 . 0) (1 . 0)
(-1 . 1) (0 . 1) (1 . 1)))
然后你的功能就像
一样简单(defun get-neighbours (board x y)
(mapcar (lambda (offs)
(let ((dx (car offs))
(dy (cdr offs)))
(get-if-possible board (+ x dx) (+ y dy))))
+neighbour-offsets+))
答案 1 :(得分:2)
首先,你仍然在势在必行的土地。
(defun get-neighbours (board x y)
(let ((output '() ))
(push (get-if-possible board (1+ x) y) output)
(push (get-if-possible board (1- x) y) output)
(push (get-if-possible board x (1+ y)) output)
(push (get-if-possible board x (1- y)) output)
(push (get-if-possible board (1+ x) (1+ y)) output)
(push (get-if-possible board (1- x) (1- y)) output)
(push (get-if-possible board (1+ x) (1- y)) output)
(push (get-if-possible board (1- x) (1+ y)) output)
output))
你做:变量声明,将它绑定到NIL,变量的变异,返回变量。
简单地说:
(defun get-neighbours (board x y)
(list (get-if-possible board (1+ x) y)
(get-if-possible board (1- x) y)
(get-if-possible board x (1+ y))
(get-if-possible board x (1- y))
(get-if-possible board (1+ x) (1+ y))
(get-if-possible board (1- x) (1- y))
(get-if-possible board (1+ x) (1- y))
(get-if-possible board (1- x) (1+ y))))
您可以使用本地宏“缩短”代码:
(defun get-neighbours (board x y)
(macrolet ((all-possible (&rest x-y-combinations)
`(list
,@(loop for (a b) on x-y-combinations by #'cddr
collect `(get-if-posssible board ,a ,b)))))
(all-possible (1+ x) y
(1- x) y
x (1+ y)
x (1- y)
(1+ x) (1+ y)
(1- x) (1- y)
(1+ x) (1- y)
(1- x) (1+ y))))
现在可以抽象一下:
(defmacro invoke-fn-on (fn &rest x-y-combinations)
`(funcall (function ,fn)
,@(loop for (a b) on x-y-combinations by #'cddr
collect `(get-if-posssible board ,a ,b))))
(defun get-neighbours (board x y)
(invoke-fn-on list
(1+ x) y
(1- x) y
x (1+ y)
x (1- y)
(1+ x) (1+ y)
(1- x) (1- y)
(1+ x) (1- y)
(1- x) (1+ y)))
关于LOOP:
> (loop for (a b) on '(1 2 3 4 5 6) by #'cddr collect (list a b))
((1 2) (3 4) (5 6))
ON将模式(a b)移到列表上(1 2 3 4 5 6)。它将给出(1 2),(2 3),(3 4),(4 5)和(5 6)。每次迭代时,使用CDR获取剩余列表,将列表向前移动一个。 BY现在说我们通过CDDR而不是像CDR一样移动两个项目。所以我们得到三次迭代和对(1 2),(3 4)和(5 6)。
另一种方法是通过为坐标对引入不同的列表结构来略微简化LOOP:
(defmacro invoke-fn-on (fn x-y-combinations)
`(funcall (function ,fn)
,@(loop for (a b) in x-y-combinations
collect `(get-if-posssible board ,a ,b))))
(defun get-neighbours (board x y)
(invoke-fn-on list
'(((1+ x) y )
((1- x) y )
(x (1+ y))
(x (1- y))
((1+ x) (1+ y))
((1- x) (1- y))
((1+ x) (1- y))
((1- x) (1+ y)))))