Во-первых, вы все еще находитесь в императивной земле.
(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 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)))))
person
Rainer Joswig
schedule
26.04.2011