Рекурсивная lisp-функция для решения N-Queen

ОБНОВЛЕНО: Теперь код должен компилироваться без ошибок или предупреждений. Извините за предыдущий. Проблема, с которой я сталкиваюсь сейчас, заключается в том, что при запуске (или с любым другим целым числом)

(NxNqueen-solver 10)

Функция getqueencol вернет nil, потому что на доске изначально нет ферзей, следовательно, в здесь-можно-поместить-ферзя будет (= число nil) потому что tcol будет равен нулю. Я думаю, что это будет происходить каждый раз, когда в строке, переданной в качестве аргумента функции ферзь, которую можно разместить, нет ферзя.

Поделитесь, пожалуйста, советом, как решить эту проблему. Заранее спасибо.

Вот код

(defvar *board* (make-array '(10 10) :initial-element nil)) 


(defun getqueencol (row n)
"Traverses through the columns of a certain row
 and returns the column index of the queen."
  (loop for i below n
        do (if (aref *board* row i)
               (return-from getqueencol i))))

(defun print-board (n)
"Prints out the solution, e.g. (1 4 2 5 3),
 where 1 denotes that there is a queen at the first 
 column of the first row, and so on."
  (let ((solutionlist (make-list n)))
    (loop for row below n
          do (loop for col below n
                   do (when (aref *board* row col)
                        (setf (nth row solutionlist) col))))
    (print solutionlist)))


(defun queen-can-be-placed-here (row col n)
"Returns t if (row,col) is a possible place to put queen, otherwise nil."
  (loop for i below n
       do (let ((tcol (getqueencol i n)))
            (if (or (= col tcol) (= (abs (- row i)) (abs (- col tcol))))
                (return-from queen-can-be-placed-here nil)))))



(defun backtracking (row n)
"Solves the NxN-queen problem with backtracking"
  (if (< row n)
      (loop for i below n
          do (when (queen-can-be-placed-here row i n)
                  (setf (aref *board* row i) 't)
                  (return-from backtracking (backtracking (+ row 1) n))
                  (setf (aref *board* row i) 'nil))
    (print-board n))))

(defun NxNqueen-solver (k)
"Main program for the function call to the recursive solving of the problem"
  (setf *board* (make-array '(k k) :initial-element nil))
  (backtracking 0 k))

person Richard Berg    schedule 23.09.2015    source источник
comment
У вас есть несколько ошибок в коде, так как вы можете сказать, что функции компилируются? Например, у вас есть (return-from getmarkedcol) внутри getqueencol; вы делаете (setq solutionlist ...) без определения solutionlist, в (let (tcol (getqueencol i)) есть искаженный let и т. д. Более того, вы не должны использовать defvar внутри функции.   -  person Renzo    schedule 23.09.2015
comment
@Ренцо Спасибо. Пожалуйста, посмотрите на мой ответ ниже. Код компилируется, но получает несвязанную переменную   -  person Richard Berg    schedule 23.09.2015
comment
Кстати, лучше передать плату в функции и не использовать для нее глобальную переменную.   -  person Rainer Joswig    schedule 24.09.2015
comment
Подумайте над этими вопросами: 1. В чем разница между например, '(pi pi) aka. (quote pi pi) и (list pi pi)? 2. Сколько ферзей нужно учитывать при размещении i-го ферзя? 3. Что заставляет queen-can-be-placed-here возвращать что угодно, кроме нуля? 4. Какова цель вызова return-from в backtracking? Надеемся, что после этого программа заработает, и вы сможете получить пользу от отправки ее на codereview.stackexchange.com.   -  person Terje D.    schedule 24.09.2015
comment
Спасибо за помощь. Теперь я понимаю разницу между '(pi pi) и (list pi pi). Вопрос 3: Я добавил (return-from queen-can-be-placed-here t) вне цикла, чтобы он также мог возвращать TRUE. Вопрос 4: Я удалил оператор return-from в backtracking. Я не думаю, что есть цель завершить код на этом этапе. Вопрос 2: Поскольку я использую возврат, должно быть достаточно рассмотреть i-го - 1 ферзя, верно? Но это были у меня проблемы. Мой getqueencol всегда возвращает nil, когда в определенном ряду нет ферзя... Пожалуйста, помогите мне!   -  person Richard Berg    schedule 25.09.2015
comment
Тогда не вызывайте getqueencol в рядах, где еще нет ферзя.   -  person Terje D.    schedule 25.09.2015
comment
@TerjeD. Не могли бы вы предложить способ сделать это? Я пробовал зацикливать столбцы определенной строки в queen-can-be-placed-here и проверять, есть ли там королева или нет. То, что я не   -  person Richard Berg    schedule 25.09.2015
comment
@TerjeD. Не обращайте внимания на первый комментарий. Спасибо за ваш ответ. Итак, теперь у меня есть переменная в queen-can-be-placed-here, которая знает, есть ли в определенной строке ферзь или нет. Но какую проверку я могу сделать, чтобы определить, является ли это возможной позицией для ферзя? Я не могу использовать тот, который уже написан в queen-can-be-placed-here, я полагаю, потому что он использует tcol, который нельзя использовать, если в ряду нет ферзя. заранее спасибо   -  person Richard Berg    schedule 25.09.2015
comment
Чтобы не вызывать getqueencol напрасно: Может стоит просто зациклить below row в queen-can-be-placed-here.   -  person Terje D.    schedule 25.09.2015
comment
Большое спасибо @TerjeD. !!! Кажется, теперь это работает. Чего я не понимаю, так это того, что моя программа находит ВСЕ решения проблемы. Я думал, что он просто найдет один, а затем напечатает его. Вы понимаете, почему он находит все решения, а не ограничивается одним? :D   -  person Richard Berg    schedule 26.09.2015
comment
Вы должны сделать это новым вопросом.   -  person Terje D.    schedule 27.09.2015


Ответы (1)


Вы говорите, что скомпилировали свой код. Этого не может быть, так как тогда вы бы увидели, что компилятор жалуется на ошибки. Вы хотите убедиться, что вы действительно компилируете код и исправляете код, чтобы он компилировался без ошибок и предупреждений.

Возможно, вы захотите избавиться от ошибок/проблем в коде (см. комментарий Ренцо), а затем посмотреть на алгоритмическую проблему. Мне очень мало смысла разбираться в алгоритмической проблеме, когда код содержит ошибки.

  • SETQ не вводит переменную, переменная должна быть где-то определена

  • DEFVAR не имеет смысла внутри функции.

  • Что-то вроде (let (x (sin a)) ...) определенно выглядит неправильно. Синтаксис LET требует пары круглых скобок вокруг списка привязок.

  • RETURN-FROM принимает в качестве первого аргумента имя существующего блока для возврата. Необязательный второй аргумент является возвращаемым значением. Получите правильный синтаксис и вернитесь из правильного блока.

  • в вызове MAKE-ARRAY укажите значение по умолчанию: (make-array ... :initial-element nil), а то не понятно что это такое.

  • Переменная *board* не определена

Стиль

  • в LOOP: for i to (1- n) проще for i below n

  • вам не нужно заключать в кавычки NIL и T.

  • (if (eq foo t) ...) может быть проще записано как (if foo ...). Особенно, если значение foo равно NIL или T.

  • (if foo (progn ...)) это просто (when foo ...)


Я не уверен, что вы делаете, чтобы утверждать, что ваш код компилируется. Он не компилируется.

Каждая функция имеет предупреждения компилятора. Вы должны проверить предупреждения компилятора и устранить проблемы.

(defun getqueencol (row)
"Traverses through the columns of a certain row
 and returns the column index of the queen."
  (loop for i below n
        do (if (aref board row i)
               (return-from getqueencol i))))

Компилятор жалуется:

;;;*** Warning in GETQUEENCOL: N assumed special
;;;*** Warning in GETQUEENCOL: BOARD assumed special

Где определяется n? Откуда board?

(defun print-board (board)
"Prints out the solution, e.g. (1 4 2 5 3),
 where 1 denotes that there is a queen at the first 
 column of the first row, and so on."
  (let (solutionlist)
    (setq solutionlist (make-list n)))
  (loop for row below n
        do (loop for col below n
               do (when (aref board row col)
                      (setf (nth row solutionlist) col))))
  (print solutionlist))

LET не имеет смысла. (let (foo) (setq foo bar) ...) это (let ((foo bar)) ...).

Почему список решений не определен? Посмотрите на LET... это не имеет смысла.

Откуда n?

(defun queen-can-be-placed-here (row col)
"Returns t if (row,col) is a possible place to put queen, otherwise nil."
  (loop for i below n
       do (let (tcol)
            (setq tcol (getqueencol i)))
       (if (or (= col tcol) (= (abs (- row i)) (abs (- col tcol))))
          (return-from queen-can-be-placed-here nil))))

откуда n? LET не имеет смысла.

(defun backtracking (row)
"Solves the NxN-queen problem with backtracking"
  (if (< row n)
      (loop for i below n
          do (when (queen-can-be-placed-here row i)
                  (setf (aref board row i) 't)
                  (return-from backtracking (backtracking (+ row 1)))
                  (setf (aref board row i) 'nil))
    (print-board board))))

Откуда n? Где определяется board?

(defun NxNqueen-solver (k)
"Main program for the function call to the recursive solving of the problem"
    (let (n board)
          (setq n k)
          (setq board (make-array '(k k) :initial-element nil)))
        (backtracking 0))

Зачем использовать setq, если у вас есть let? Локальные переменные n и board не используются.

MAKE-ARRAY ожидает список чисел, а не список символов.

Я предлагаю вам использовать базовое введение в Лисп (Common Lisp: A Gentle Introduction to Symbolic Computation). — бесплатная загрузка) и справочник по Лиспу (CL Hyperspec ).

person Rainer Joswig    schedule 23.09.2015
comment
К OP: попробуйте выяснить, почему вам не нужно цитировать NIL и T. - person coredump; 23.09.2015
comment
Привет Райнер. Пожалуйста, посмотрите на мой ответ ниже. Я обновил код в соответствии с вашим ответом, и теперь он компилируется, но есть несвязанная переменная. - person Richard Berg; 23.09.2015
comment
@RichardBerg: смотрите мои обновления. Компилятор показывает много предупреждений. Исправьте их сначала... - person Rainer Joswig; 23.09.2015
comment
@RainerJoswig Привет, Райнер. Пожалуйста, смотрите мою отредактированную тему. Теперь код соответствует требованиям без каких-либо предупреждений или ошибок. - person Richard Berg; 24.09.2015
comment
@RainerJoswig Вы имеете в виду неправильно, как будто он не компилируется? Я выделил код в своем редакторе и попробовал Buffer, Definition, File › Compile, и ни один из них не говорит об ошибке или предупреждении. Все, что он говорит, это -----Готово----- или ------Нажмите пробел, чтобы продолжить----------. Я правильно компилирую? Извините за глупые вопросы, и я искренне ценю ваше терпение :) - person Richard Berg; 24.09.2015
comment
@RichardBerg: '(k k) — это список из двух символов. Не то, что хочет Лисп. Компилятор LispWorks не жалуется, просто не запускается...: Bad argument to MAKE-ARRAY: (K K). --- Я не даю вам прямого ответа, вы должны его узнать. Я надеюсь, это нормально? ;-) - person Rainer Joswig; 24.09.2015
comment
@RainerJoswig Привет, Райнер, и спасибо за всю твою помощь. Программа наконец-то работает как надо. Что мне интересно, так это то, как я могу изменить backtracking, чтобы программа завершилась, как только она нашла и напечатала только ОДНО решение. Прямо сейчас он находит все возможные решения, я думаю, это потому, что каждый раз, когда он возвращается из вызова, все еще есть ожидающие циклы на разных уровнях рекурсии. У вас есть идеи, как выйти из backtracking, чтобы у меня было только 1 решение? - person Richard Berg; 27.09.2015