2011-04-25 59 views
3

讓我們假設我有一個二維表(或數組,如果你願意的話),它看起來是這樣的:查找2D名單鄰居,聰明的方法

'((I I I O) 
    (I X I O) 
    (I I I O)) 

現在讓我們假設我d喜歡找到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

首先,你仍然在勢在必行土地

(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獲取剩餘列表,將列表向前移動一個。現在說,我們移動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))))) 
+0

嗯,好吧,循環宏中關鍵字「by」和「on」的含義對我而言仍然是未知的。我仍然比較喜歡安格斯的答案,但我仍然非常感謝你的幫助! – Johan 2011-04-26 21:12:39

+0

我不知道你可以用循環做這樣複雜而有用的事情。有投票權! – whoplisp 2011-07-04 00:42:43

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+)) 
+0

哦,是的,那將是更漂亮,但仍然保留的方式簡單起見,我寫的。謝謝! – Johan 2011-04-25 21:09:22

+1

警告詞:注意將(文字)列表定義爲常量。有些實現會安靜地容忍它(Lispworks),其他實現會不斷地嘗試重新定義常量+ foo +'(sbcl) http://www.sbcl.org/manual/Defining-Constants.html – Beef 2011-04-26 06:21:08