Неисправность генератора судоку, лисп - PullRequest
2 голосов
/ 10 ноября 2009

У меня проблема с какой-то частью моего кода 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, и мне нужно вставить элемент.Я наблюдаю подматрицу для этого элемента и для столбца, и я выбираю число, которое не появляется ни в одном из вышеперечисленных, и помещаю его туда.Это переменная «выбор».Проблема в том, что иногда переменная "choices" получает значение NIL, хотя в цикле ввода она имеет правильное значение.Когда он получает NIL, значение выбора остается таким же, как и в последнем цикле (я зациклен на столбцах и строках над этим фрагментом), в результате чего в моей окончательной таблице вывод недействительных данных (например, двойные значения в строке).Как я могу отследить, где меняется переменная выбора?Я работаю с ним только в этом фрагменте и не понимаю, почему он внезапно меняется на ноль.

Например, у меня обычно есть:

  • в цикле записей: выборы (5)
  • Цикл вне записей: выборы (5)
  • в цикле записей: выборы (6 7)
  • Цикл вне записей: выборы (6 7) и после этоговот этот:
  • в цикле ввода: варианты выбора ноль.

Спасибо.

Ответы [ 4 ]

3 голосов
/ 10 ноября 2009

Сначала небольшое переформатирование:

(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 после этого фрагмента, и что печать 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)))))

Обновление по запросу.

Как указывает Райнер, это в основном тело функции 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)
1 голос
/ 10 ноября 2009

Мой Лисп довольно ржавый, но я не вижу здесь никакого отступления ... и я думаю, что вы не можете просто начать добавлять числа случайным образом и ожидать, что они сделают правильную игру судоку.

Кажется, что список равен нулю, потому что нет никаких возможных опций и, следовательно, он не создан. Вы должны справиться с этим.

1 голос
/ 10 ноября 2009

Потенциальный источник проблем - 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, ...)
0 голосов
/ 10 ноября 2009

Это неправильный ответ, но я исправил отступ, чтобы сделать код немного более разборчивым для себя и других отвечающих:

(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' установлены в?
...