我的lisp代码的某些部分存在问题。它是一个数独表生成器。它工作正常,直到这部分:
(loop for e in entries do
(if (and (not (member e sub))
(not (member e col)))
(progn (setq choices (nconc choices (list e)))
(print choices)))
(if (= (length choices) 1)
(setq pick (car choices))
(if (not (= (length choices) 0))
(setq pick (nth (random (+ 0 (length choices))) choices))))
基本上,我在第x行和第y列,我需要插入一个元素。我观察这个元素和列的子矩阵,我选择上面没有出现的数字并把它放在那里。那是“挑选”变量。问题是有时“选择”变量获得NIL值,尽管在条目循环中它具有正确的值。当它获得NIL时,pick值保持与上一个循环相同(我在这个片段上方的列和行中循环),使得我的final表具有无效的输出(例如,连续的双值)。如何跟踪选项变量的位置?我只在这个片段中使用它,我不明白为什么它突然变为零。
例如,我通常有:
谢谢。
答案 0 :(得分:3)
首先,一些重新格式化:
(loop for e in entries do
(if (and (not (member e sub))
(not (member e col)))
(progn (setq choices (nconc choices (list e)))
(print choices)))
(if (= (length choices) 1)
(setq pick (car choices))
(if (not (= (length choices) 0))
(setq pick (nth (random (+ 0 (length choices))) choices))))
然后,如果您不需要if
的替代条款,但需要progn
,则可以使用when
:
(loop for e in entries do
(when (and (not (member e sub))
(not (member e col)))
(setq choices (nconc choices (list e)))
(print choices))
(if (= (length choices) 1)
(setq pick (car choices))
(if (not (= (length choices) 0))
(setq pick (nth (random (+ 0 (length choices))) choices))))
最后两个if
条款是互斥的,因此cond
或case
是合适的(我现在会使用cond
):
(loop for e in entries do
(when (and (not (member e sub))
(not (member e col)))
(setq choices (nconc choices (list e)))
(print choices))
(cond ((= (length choices) 1)
(setq pick (car choices)))
((not (= (length choices) 0))
(setq pick (nth (random (+ 0 (length choices))) choices))))
有一个zerop
谓词:
(loop for e in entries do
(when (and (not (member e sub))
(not (member e col)))
(setq choices (nconc choices (list e)))
(print choices))
(cond ((= (length choices) 1)
(setq pick (car choices)))
((not (zerop (length choices)))
(setq pick (nth (random (+ 0 (length choices))) choices))))
我没有看到添加0到某个值应该完成的是什么:
(loop for e in entries do
(when (and (not (member e sub))
(not (member e col)))
(setq choices (nconc choices (list e)))
(print choices))
(cond ((= (length choices) 1)
(setq pick (car choices)))
((not (zerop (length choices)))
(setq pick (nth (random (length choices)) choices))))
除非你确定pick
设置为合理的默认值,否则你应该有一个默认情况(这可能是你的问题之一):
(loop for e in entries do
(when (and (not (member e sub))
(not (member e col)))
(setq choices (nconc choices (list e)))
(print choices))
(cond ((= (length choices) 1)
(setq pick (car choices)))
((not (zerop (length choices)))
(setq pick (nth (random (length choices)) choices)))
(t
(setq pick nil))
不是使用setq
和nconc
,而是可以使用push
(这会将新元素放在列表的开头,但是因为无论如何都要随机选择,这不应该关注):
(loop for e in entries do
(when (and (not (member e sub))
(not (member e col)))
(push e choices)
(print choices))
(cond ((= (length choices) 1)
(setq pick (car choices)))
((not (zerop (length choices)))
(setq pick (nth (random (length choices)) choices)))
(t
(setq pick nil))
我怀疑在此代码段的开头,choices
应该是()
,在此代码段之后您不需要choices
,而且打印{{1} }仅用于调试,因此您可以使用choices
并更改条件以不同的方式执行此操作:
remove-if
如果(let ((choices (remove-if (lambda (e)
(or (member e sub)
(member e col)))
entries)))
(print choices)
(cond ((= (length choices) 1)
(setq pick (car choices)))
((not (zerop (length choices)))
(setq pick (nth (random (length choices)) choices)))
(t
(setq pick nil)))
现在打印为choices
,则意味着此处没有任何选择,因此您必须进行一些回溯(或者当达到死胡同时,您的算法会执行任何操作) )。
最后,由于()
只能是非负整数,如果您按不同顺序测试案例,则可以使用(length choices)
代替case
:
cond
按要求更新。
正如Rainer所指出的,这基本上是(let ((choices (remove-if (lambda (e)
(or (member e sub)
(member e col)))
entries)))
(print choices)
(case (length choices)
(0 (setq pick nil))
(1 (setq pick (car choices)))
(otherwise (setq pick (nth (random (length choices)) choices)))))
函数的主体,因此我们可以摆脱所有的自由变量。另外,您可以使用(用于列表)更具描述性的名称pick
代替car
:
first
这个函数将在其他地方定义,在代码片段中,它将被调用如下:
(defun pick (entries sub col)
(let ((choices (remove-if (lambda (e)
(or (member e sub)
(member e col)))
entries)))
(print choices)
(case (length choices)
(0 nil)
(1 (first choices))
(otherwise (nth (random (length choices)) choices)))))
为了不计算(pick entries sub col)
两次,我们可以将其放入(length choices)
(需要成为let
进行连续评估):
let*
最后一步(真的是可选的,但也许你发现你有更多的序列来减少你的选择,例如(defun pick (entries sub col)
(let* ((choices (remove-if (lambda (e)
(or (member e sub)
(member e col)))
entries))
(choices-length (length choices)))
(print choices)
(case choices-length
(0 nil)
(1 (first choices))
(otherwise (nth (random choices-length) choices)))))
)会有点概括:
row
对此函数的调用具有相同的形状,但您现在可以使用任意数量的排除序列:
(defun pick (entries &rest exclusion-sequences)
(let* ((choices (remove-if (lambda (e)
(some #'identity
(mapcar (lambda (seq)
(member e seq))
exclusion-sequences)))
entries))
(choices-length (length choices)))
(print choices)
(case choices-length
(0 nil)
(1 (first choices))
(otherwise (nth (random choices-length) choices)))))
答案 1 :(得分:1)
潜在的麻烦来源是NCONC。
NCONC的第二个问题来源是使用文字列表。
示例:
(defun foo (bar) (let ((l '(1 2 3))) ...))
这里'(1 2 3)是一个文字列表。破坏性地修改这样的列表的效果在Common Lisp中是未定义的。因此应该避免。该怎么办?
答案 2 :(得分:1)
我的Lisp相当生疏,但我没有看到任何回溯......我认为你不能随便开始随机输入数字并期望他们将进行正确的数独游戏。
似乎列表为零,因为没有可能的选项,因此没有创建。你应该处理它。
答案 3 :(得分:0)
这不是一个正确的答案,但我确实修改了缩进,使代码对我自己和其他答案者更加清晰:
(loop for e in entries do
(if (and (not (member e sub)) (not (member e col)))
(progn (setq choices (nconc choices (list e)))
(print choices) ))
(if (= (length choices) 1) (setq pick (car choices))
(if (not (=(length choices) 0))
(setq pick (nth (random (+ 0 (length choices))) choices))))
问题: