条件终止语句意外地工作

时间:2014-11-08 22:15:05

标签: recursion return common-lisp conditional-statements

我试图在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的新手所以请原谅我,如果我是一个白痴,并且遗漏了一些明显的东西。提前感谢您的帮助!

1 个答案:

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

您需要将formatreturn-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 ...)
      ...
    ))