我试图在Common LISP中实现基本的slitherlink游戏。我目前正在尝试实现一种方法来获取电路板上循环中所有行的列表。 然而,LISP递归发生了一些奇怪的事情,而且我对于为什么会发生这种情况一无所知。
我的代码看起来像这样:
(defun check-loop(loop-list to-check)
(loop for line in loop-list
do (if (and (equal (car line) (car to-check)) (equal (cadr line) (cadr to-check)))
(format t "~% Check passed for ~S in ~S" to-check loop-list)
(return-from check-loop T)))
(format t "~% Check failed for ~S in ~S" to-check loop-list)
nil)
(defun get-loop(board loop-list x y &optional (from-x -1) (from-y -1))
(let ((x-limit (array-dimension board 0))
(y-limit (array-dimension board 1)))
(if (and (equal from-x -1) (equal from-y -1))
(format t "~% x-limit=~3D y-limit=~3D" x-limit y-limit))
(format t "~% loop-list is ~S and x=~3D y=~3D from-x= ~3D from-y=~3D" loop-list x y from-x from-y)
(cond ((check-loop loop-list (list x y))
(progn (format t "~%Returned with ~S. while x =~3D y=~3D" loop-list x y)
(return-from get-loop loop-list)))
((not (check-loop loop-list (list x y)))
(setq loop-list (append loop-list (list (list x y))))
(format t "~% loop-list after append is ~S and x=~3D y=~3D" loop-list x y)
(if (and (not (< (1- x) 0)) (not (< (1- y) 0)))
(if (and (is-line board (1- x) (1- y)) (and (/= (1- x) from-x) (/= (1- y) from-y)))
(progn (format t "~%Here - 1 to ~3D ~3D" (1- x) (1- y))
(setq loop-list (get-loop board loop-list (1- x) (1- y) x y)))))
(if (and (not (< (1- x) 0)) (< (1+ y) y-limit))
(if (and (is-line board (1- x) (1+ y)) (and (/= (1- x) from-x) (/= (1+ y) from-y)))
(progn (format t "~%Here - 2 to ~3D ~3D" (1- x) (1+ y))
(setq loop-list (get-loop board loop-list (1- x) (1+ y) x y)))))
(if (and (< (1+ x) x-limit) (not (< (1- y) 0)))
(if (and (is-line board (1+ x) (1- y)) (and (/= (1+ x) from-x) (/= (1- y) from-y)))
(progn (format t "~%Here - 3 to ~3D ~3D" (1+ x) (1- y))
(setq loop-list (get-loop board loop-list (1+ x) (1- y) x y)))))
(if (and (< (1+ x) x-limit) (< (1+ y) y-limit))
(if (and (is-line board (1+ x) (1+ y)) (and (/= (1+ x) from-x) (/= (1+ y) from-y)))
(progn (format t "~%Here - 4 to ~3D ~3D" (1+ x) (1+ y))
(setq loop-list (get-loop board loop-list (1+ x) (1+ y) x y)))))
(cond ((equal (aref board x y) #\|)
(if(not (< (- x 2) 0))
(if (and (is-line board (- 2 x) y) (/= (- 2 x) from-x))
(progn (format t "~%Here - 5 to ~3D ~3D" (- x 2) y)
(setq loop-list (get-loop board loop-list (- 2 x) y x y)))))
(if(< (+ x 2) x-limit)
(if (and (is-line board (+ 2 x) y) (/= (+ 2 x) from-x))
(progn (format t "~%Here - 6 to ~3D ~3D" (+ x 2) y)
(setq loop-list (get-loop board loop-list (+ 2 x) y x y))))))
((equal (aref board x y) #\-)
(if(not (< (- y 2) 0))
(if (and (is-line board x (- 2 y)) (/= (- 2 y) from-y))
(progn (format t "~%Here - 7 to ~3D ~3D" x (- y 2))
(setq loop-list (get-loop board loop-list x (- 2 y) x y)))))
(if(< (+ y 2) y-limit)
(if (and (is-line board x (+ 2 y)) (/= (+ 2 y) from-y))
(progn (format t "~%Here - 8 to ~3D ~3D" x (+ y 2))
(setq loop-list (get-loop board loop-list x (+ 2 y) x y)))))))))
(format t "~% get-loop end with ~3D ~3D" x y )
(return-from get-loop loop-list)))
is-line函数仅检查所讨论的单元格是否为&#34; |&#34;或者&#34; _&#34;。 check-loop函数检查特定索引列表是否是循环列表的一部分。 get-loop函数尝试获取所有索引对的列表,这些索引对作为循环的一部分,当它被给定一行开始时有一行。该函数使用一行(数组的一对索引)递归调用自身,然后通过跟随当前行的下一行调用自身。
我用以下板(数组)调用get-loop,循环列表为NIL,x和y分别为1和0,用于下面的板。
+ - + - +
| 3 3 |
+ - + - +
我得到的输出如下:
x-limit= 3 y-limit= 5
loop-list is NIL and x= 0 y= 1 from-x= -1 from-y= -1
Check failed for (0 1) in NIL
Check failed for (0 1) in NIL
loop-list after append is ((0 1)) and x= 0 y= 1
Here - 3 to 1 0
loop-list is ((0 1)) and x= 1 y= 0 from-x= 0 from-y= 1
Returned with ((0 1)). while x = 1 y= 0
Here - 8 to 0 3
loop-list is ((0 1)) and x= 0 y= 3 from-x= 0 from-y= 1
Returned with ((0 1)). while x = 0 y= 3
get-loop end with 0 1
Finished. List is ((0 1))
我不明白的是,如果没有执行return
,第一个check-loop
将被执行,因为check-loop
的打印语句未被执行。另请注意,输出中最后一个完成的行由get-loop的调用者打印。我仍然是lisp的新手所以请原谅我,如果我是一个白痴,并且遗漏了一些明显的东西。提前感谢您的帮助!
答案 0 :(得分:0)
check-loop
中的第一行未通过测试,则 loop-list
不会打印任何内容。
当你进入loop for line in loop-list
时,它会测试每一行。如果第一行未通过if
测试,则会跳过check passed
行,并执行(return-from check-loop T)
。所以你永远不会打印check failed
。
如果您的if
中不需要else子句,请改用when
:
(defun check-loop(loop-list to-check)
(loop for line in loop-list
do (when (and (equal (car line) (car to-check)) (equal (cadr line) (cadr to-check)))
(format t "~% Check passed for ~S in ~S" to-check loop-list)
(return-from check-loop T)))
(format t "~% Check failed for ~S in ~S" to-check loop-list)
nil)
您需要将format
和return-from
放入progn
,以便在测试成功时完成,而不仅仅是format
。
顺便说一句,如果line
只有两个元素,您可以将其简化为if (equal line to-check)
。
此外,在get-loop
中,无需再拨打check-loop
两次。如果它到达cond
的第二个子句,则表示check-loop
失败,因此您无需测试(not (check-loop ...))
。写:
(cond ((check-loop ...)
(format ...)
(return-from ...))
(t (setq ...)
...))
或者,由于只有两个分支,请使用if
:
(if (check-loop)
(progn
(format ...)
(return-from ...))
(progn
(setq ...)
...
))