数独表生成器失败,lisp

时间:2009-11-09 22:32:29

标签: lisp sudoku

我的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表具有无效的输出(例如,连续的双值)。如何跟踪选项变量的位置?我只在这个片段中使用它,我不明白为什么它突然变为零。

例如,我通常有:

  • 在条目循环中:选项(5)
  • 输入循环:选择(5)
  • 在条目循环中:选择(6 7)
  • 输入循环:选择(6 7),然后是这个:
  • 在条目循环中:选择nil。

谢谢。

4 个答案:

答案 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条款是互斥的,因此condcase是合适的(我现在会使用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))

不是使用setqnconc,而是可以使用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正在破坏性地修改第一个列表。如果这是不需要的,请改用APPEND。

NCONC的第二个问题来源是使用文字列表。

示例:

(defun foo (bar)  (let ((l '(1 2 3))) ...))

这里'(1 2 3)是一个文字列表。破坏性地修改这样的列表的效果在Common Lisp中是未定义的。因此应该避免。该怎么办?

  1. 缺点清单:(清单1 2 3)
  2. 复制文字列表:( copy-list l)
  3. 使用非破坏性操作(APPEND代替NCONC,......)

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

问题:

  1. 条目是列表吗?每个列表是否代表一行?
  2. 'sub'和'col'设置为什么值?