这里有什么问题?我试图增加值,但输出为 0

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

Num*_*bra 5

在回答实际问题之前,请先了解一些关于风格和一般性评论的注释:

  • 特殊变量(即由defvar或引入defparameter)通常以以 * 开头和结尾的名称命名(例如,(defvar *m* 4)

  • 除非你做了一些非常奇怪的修改,否则读者不会区分大小写。这意味着M-DASHm-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,则结果是未定义的。