2009-11-09 3 views
2

У меня проблема с некоторой частью моего кода 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)))) 

В принципе, я на строке х и колонки у, и мне нужно вставить элемент. Я смотрю подматрицу для этого элемента и столбца, и я выбираю число, которое не появляется ни в одном из вышеперечисленных, и помещаем его туда. Это переменная «pick». Проблема в том, что иногда переменная «выбор» получает значение NIL, хотя в петлях записей оно имеет правильное значение. Когда он получает NIL, значение выбора остается таким же, как и в последнем цикле (я перебираю столбцы и строки выше этого фрагмента), в результате чего моя итоговая таблица имеет недействительный вывод (например, двойные значения в строке). Как я могу отслеживать, где изменяется переменная выбора? Я работаю с ним только в этом фрагменте, и я не понимаю, почему он внезапно меняется на ноль.

Например, я обычно:

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

спасибо.

ответ

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» для?
+0

Нет, записи - это список элементов, не найденных в sub и col. Col представляет столбец, в котором я находится в текущем цикле, а sub представляет собой подматрицу платы. Имея

 (1 1 1 1 1) (2 2 2 2 2) (3 3 3 3 -1) 
как таблица, для итерации 3, col представляют (1 2 -1) и sub ((2 2 2) (3 3 -1)) – Manticore

1

Потенциальным источником проблем является NCONC.

  • nconc разрушительно модифицирует первый список. Если это нежелательно, вместо этого используйте APPEND.

Второй источник проблем с NCONC - использование литеральных списков.

Пример:

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

Здесь «(1 2 3) является буквальным список. Эффекты деструктивного изменения такого списка не определены в Common Lisp. Таким образом, этого следует избегать. Что делать вместо этого?

  1. против списка: (список 1 2 3)
  2. скопировать буквального список: (копирование списка л)
  3. использовать не деструктивные операции (APPEND вместо NCONC, ...)
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 после этого фрагмента кода, и что печать 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))))) 

Update по запросу.

Как отмечает Райнер, это в основном тело функции 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) 
+0

еще: получите там SETQ. В основном этот фрагмент является телом функции PICK. Также ПЕРВЫЙ вместо CAR. Не вычисляйте ДЛИНА дважды. –

1

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

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

Смежные вопросы