2

我的 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 列上,我需要插入一个元素。我观察这个元素和列的子矩阵,然后选择上面任何一个中没有出现的数字并将其放在那里。这就是“pick”变量。问题是有时“选择”变量会获得 NIL 值,尽管在条目循环中它具有正确的值。当它获得 NIL 时,选择值与上一个循环中的值保持不变(我在此代码段上方的列和行中循环),使我的最终表的输出无效(例如,一行中的双值)。如何跟踪选择变量的变化?我只在这个片段中使用它,我不明白为什么它突然变成零。

例如,我通常有:

  • 在条目循环中:选择 (5)
  • 条目外循环:选择 (5)
  • 在条目循环中:选择 (6 7)
  • 退出条目循环:选择(6 7),然后是这个:
  • 在条目循环中:选择无。

谢谢你。

4

4 回答 4

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,但想要 a 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))

您可以使用而不是使用setqand (这会将新元素放在列表的开头,但是由于您无论如何都是随机选择的,因此不必担心):nconcpush

(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,并且打印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

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

按要求更新

正如 Rainer 指出的,这基本上是一个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)))))

对该函数的调用具有相同的形式,但您现在可以使用任意数量的排除序列:

(pick entries col sub row ver ima fou)
于 2009-11-10T08:51:59.063 回答
1

我的 Lisp 已经很生疏了,但我没有看到任何回溯……而且我认为你不能只是开始随机输入数字并期望它们会做出适当的数独游戏。

似乎该列表为零,因为没有可能的选项,因此没有创建。你应该处理那个。

于 2009-11-10T09:01:18.327 回答
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,...)
于 2009-11-10T00:09:49.647 回答
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' 的值是什么?
于 2009-11-09T22:44:14.940 回答