以2d-list,智能方式查找邻居

时间:2011-04-25 10:32:19

标签: common-lisp

让我们假设我有一个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))

就是这样......丑陋。

2 个答案:

答案 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)))))