-1 lisp common-lisp
已经花了几个小时了,我是 LISP 新手,无法弄清楚这有什么问题。我正在尝试使用回溯来解决矩阵问题。有人可以帮忙吗?
(defvar m 3)
(defvar n 4)
(defvar M-DASH (make-array (list m n)
:initial-contents '((1 0 1 0) (0 1 1 0) (1 0 1 1))))
(defvar selected (make-array n))
(defvar set-count 0)
(defun cand-set-count (M-DASH set-count selected curr)
(if (>= curr m)
(progn
(incf set-count)
(return-from cand-set-count 0)
))
(loop for i from 0 to (- n 1)
do (if (and (equal (aref M-DASH curr i) 1) (null (aref selected i)))
(progn
(setf (aref selected i) 1)
(cand-set-count M-DASH set-count selected (+ curr 1))
(setf (aref selected i) nil)
))))
(cand-set-count M-DASH set-count selected 0)
(format t "~A" set-count)
Run Code Online (Sandbox Code Playgroud)
在回答实际问题之前,请先了解一些关于风格和一般性评论的注释:
特殊变量(即由defvar或引入defparameter)通常以以 * 开头和结尾的名称命名(例如,(defvar *m* 4)
除非你做了一些非常奇怪的修改,否则读者不会区分大小写。这意味着M-DASH和m-dash是相同的符号。因此,我们倾向于不使用任何形式的大写,因为它可能会导致一些错误(例如,认为两个符号不同,而实际上它们是相同的......)。
如果您使用 aif但只关心 thethen或 the elsepart,请when/unless改用。它使意图更加清晰,并且还带有所谓的“隐式 progn”:
(if some-test (progn (do-smtg) (do-smtg-else)))
与
(当进行某些测试(do-smtg)(do-smtg-else)时
如果您只是从 0 循环到某个数字,并且没有做非常复杂的事情(或者,如果您是初学者,并且不想处理宏的特定语法规则loop...),请改用dotimes。它更具可读性,更少混乱,并且通常更不容易出错。
考虑到这一点,您的主要功能可以重写为:
(defun cand-set-count (m-dash set-count selected curr)
(when (>= curr m)
(incf set-count)
(return-from cand-set-count 0))
(dotimes (i n)
(when (and (equal (aref m-dash curr i) 1)
(null (aref selected i)))
(setf (aref selected i) 1)
(cand-set-count m-dash set-count selected (+ curr 1))
(setf (aref selected i) nil))))
Run Code Online (Sandbox Code Playgroud)
这绝对更清楚。
现在,真正的问题是:该生产线的(incf set-count)功能与您想象的不同。它不会增加名为 set-count 的特殊变量的值...如果您遵循在名称周围用 * 命名特殊变量的约定(我不是在这里讽刺:它是(其中之一),那就很清楚了...)约定存在的原因)。递增的是本地绑定,而不是特殊变量的值。您可以尝试使用更简单的代码:
* (defvar *foo* 0)
0
* (defun bar (foo)
(incf foo))
BAR
* (bar *foo*)
1
* *foo*
0
Run Code Online (Sandbox Code Playgroud)
换句话说:您有一个名为 的特殊(因此,在某种意义上是全局)变量set-count,但您的函数也接受一个名为 的参数set-count!您想要纯功能且无副作用,还是想要改变状态并传递全局引用?两者都可以,但你需要坚持下去。
m-dash对于其他参数和也是如此selected,它们都“隐藏”在函数体内,并且从未实际修改过。
另一个可能会在以后困扰您的错误是:默认情况下不会初始化数组。你不能假设将(defvar selected (make-array n))任何东西初始化为 0 或nil; 您的函数稍后会测试 if (null (aref selected i)),但如果您没有显式初始化selectedwith :initial-contents/:initial-element,则结果是未定义的。