为什么我的 n-queens 代码返回空列表?

问题描述

我目前正在处理 SICP Exercise 2.42。我知道我的 adjoin-position 函数被窃听了,因为当我用

替换它时
(define (adjoin-position-WORKING new-row k rest-of-queens) 
   (cons (list new-row k) rest-of-queens))

我从 queens 得到了合理的输出

在此替换之前,我使用练习模板的代码如下:

#lang sicp
;;Boilerplate functions that the exercise calls:
(define (filter predicate sequence)
  (cond ((null? sequence) nil)
        ((predicate (car sequence))
         (cons (car sequence)
               (filter predicate (cdr sequence))))
        (else (filter predicate (cdr sequence)))))
(define (enumerate-interval low high) 
  (cond ((> low high) nil) 
        ((= low high) (list high)) 
        (else (cons low (enumerate-interval (+ 1 low) high)))))
(define (flatmap proc seq)
  (accumulate append nil (map proc seq)))
(define (accumulate op initial sequence)
  (if (null? sequence)
      initial
      (op (car sequence)
          (accumulate op initial (cdr sequence)))))

;;;Start of solution:
(define empty-board nil)
;Placeholder,stops the filter from actually filtering anything:
(define (safe? new-queen-col-numb old-solutions) #t)

;;THE RELEVANT BIT
(define (adjoin-position ultimate-row-index new-queen-col-pos old-solutions)
  (define (iter to-append-left to-append-right depth)
    (cond ((null? to-append-right) to-append-left)
          ((= depth 1)
           (append to-append-left (cons new-queen-col-pos to-append-right)))
          (else (iter (append to-append-left (list (car to-append-right)))
                      (cdr old-solutions)
                      (- depth 1)))))
  (iter nil old-solutions ultimate-row-index))

;;The template provided by the exercise,untouched by me:
(define (queens board-size)
  (define (queen-cols k)  
    (if (= k 0)
        (list empty-board)
        (filter
         (lambda (positions) (safe? k positions))
         (flatmap
          (lambda (rest-of-queens)
            (map (lambda (new-row)
                   (adjoin-position new-row k rest-of-queens))
                 (enumerate-interval 1 board-size)))
          (queen-cols (- k 1))))))
  (queen-cols board-size))

;;Example of adjoin-position working as expected:
(adjoin-position 2 5 (list 10 11 12 13 14 15 16))
;Output: (10 5 11 12 13 14 15 16)
;i.e. it placed 5 at position 2,as I wanted it to.

;;Code to show that my adjoin-position is bugged.
(map queens (enumerate-interval 1 3))

我的最终函数 (map queens (enumerate-interval 1 3))输出很糟糕:

((())
 (() () () ())
 (()
  ()
  ()
  ()
  ()
  ()
  ()
  ()
  ()
  ()
  ()
  ()
  ()
  ()
  ()
  ()
  ()
  ()
  ()
  ()
  ()
  ()
  ()
  ()
  ()
  ()
  ()))

adjoin-position 替换 adjoin-position-WORKING 后,我的输出更容易忍受:

((((1 1)))
 (((1 2) (1 1)) ((2 2) (1 1)) ((1 2) (2 1)) ((2 2) (2 1)))
 (((1 3) (1 2) (1 1))
  ((2 3) (1 2) (1 1))
  ((3 3) (1 2) (1 1))
  ((1 3) (2 2) (1 1))
  ((2 3) (2 2) (1 1))
  ((3 3) (2 2) (1 1))
  ((1 3) (3 2) (1 1))
  ((2 3) (3 2) (1 1))
  ((3 3) (3 2) (1 1))
  ((1 3) (1 2) (2 1))
  ((2 3) (1 2) (2 1))
  ((3 3) (1 2) (2 1))
  ((1 3) (2 2) (2 1))
  ((2 3) (2 2) (2 1))
  ((3 3) (2 2) (2 1))
  ((1 3) (3 2) (2 1))
  ((2 3) (3 2) (2 1))
  ((3 3) (3 2) (2 1))
  ((1 3) (1 2) (3 1))
  ((2 3) (1 2) (3 1))
  ((3 3) (1 2) (3 1))
  ((1 3) (2 2) (3 1))
  ((2 3) (2 2) (3 1))
  ((3 3) (2 2) (3 1))
  ((1 3) (3 2) (3 1))
  ((2 3) (3 2) (3 1))
  ((3 3) (3 2) (3 1))))

所以,既然我们肯定知道原来的 adjoin-position 被窃听并返回了一个空列表的列表,而不是包括数字在内的任何内容,那么我真正的问题是 - 原来的 {{1} }} 出问题?对我来说最大的惊喜不仅是 adjoin-position 工作得很好,而且 (adjoin-position 2 5 (list 10 11 12 13 14 15 16)) 应该调用我理解的 queens,这也给出了一个可用的结果:{{1} }.我是否误解了 (adjoin-position 1 1 (list nil)) 传递给 (1 ()) 的参数?

我还没有学会如何调试 Scheme 代码,而是将调用 queens 替换为调用 adjoin-position

(map (lambda (new-row)...

map-debug

生成以下输出
(define (map-debug proc seq)
  (display "Input list: ")
  (display seq)
  (newline)
  (display "Output list:")
  (display (map proc seq))
  (newline)
  (map proc seq))

暗示该错误在以下代码块中,但我没有看到。

(map queens (enumerate-interval 1 2))

我能弄清楚的最远情况是 Input list: (1) Output list:(()) Input list: (1 2) Output list:(() ()) Input list: (1 2) Output list:(() ()) Input list: (1 2) Output list:(() ()) ((()) (() () () ())) (lambda (rest-of-queens) (map (lambda (new-row) (adjoin-position new-row k rest-of-queens)) (enumerate-interval 1 board-size))) 语句的 (= depth 1) 分支似乎永远不会运行。我不知道为什么,我只知道在其中粘贴 adjoin-position 行表明该分支从未被使用过。似乎它的 cond 参数正在传递 display?

解决方法

首先让我描述一下我如何理解该程序的目的。因为如果我们对它应该如何工作有不同的想法,那么理解实现就很困难。

假设我们已经有了 1-Column 和 2-Column 问题的第一个解决方案:

First of 8 Solutions To the 1-Column Problem:
=============================================
   _ 
  |_|
  |_|
  |_|
  |_|
  |_|
  |_|
  |_|
  |Q|

position: ((1 1))


First of 42 Solutions To the 2-Column Problem:
==============================================
   _ _ 
  |_|_|
  |_|_|
  |_|_|
  |_|_|
  |_|_|
  |_|Q|
  |_|_|
  |Q|_|
  
position: ((2 3) (1 1))

对于 3-Column 问题,我们从 2-Column 问题的第一个解决方案开始,然后为 8 个可能的行生成 8 个可能的棋盘位置,皇后可以放置在第 3 列中:

First 8 Positions to Test as Possible Solutions To the 3-Column Problem:
========================================================================
   _ _ _     _ _ _     _ _ _     _ _ _     _ _ _     _ _ _     _ _ _     _ _ _
  |_|_|_    |_|_|_    |_|_|_    |_|_|_    |_|_|_    |_|_|_    |_|_|_    |_|_|?
  |_|_|_    |_|_|_    |_|_|_    |_|_|_    |_|_|_    |_|_|_    |_|_|?    |_|_|_
  |_|_|_    |_|_|_    |_|_|_    |_|_|_    |_|_|_    |_|_|?    |_|_|_    |_|_|_
  |_|_|_    |_|_|_    |_|_|_    |_|_|_    |_|_|?    |_|_|_    |_|_|_    |_|_|_
  |_|_|_    |_|_|_    |_|_|_    |_|_|?    |_|_|_    |_|_|_    |_|_|_    |_|_|_
  |_|Q|_    |_|Q|_    |_|Q|?    |_|Q|_    |_|Q|_    |_|Q|_    |_|Q|_    |_|Q|_
  |_|_|_    |_|_|?    |_|_|_    |_|_|_    |_|_|_    |_|_|_    |_|_|_    |_|_|_
  |Q|_|?    |Q|_|_    |Q|_|_    |Q|_|_    |Q|_|_    |Q|_|_    |Q|_|_    |Q|_|_

First 8 positions to test:
  ((3 1) (2 3) (1 1))
  ((3 2) (2 3) (1 1))
  ((3 3) (2 3) (1 1))
  ((3 4) (2 3) (1 1))
  ((3 5) (2 3) (1 1))
  ((3 6) (2 3) (1 1))
  ((3 7) (2 3) (1 1))
  ((3 8) (2 3) (1 1))

Adjoin-position 被调用 8 次,以通过扩展 2 列解决方案来创建要测试的位置。 (造成混淆的一个可能原因是 adjoin-position 的前两个参数是 row 然后 col,但通常位置是 col 然后 row)。

Creating the first 8 positions to test:
  (adjoin-position 1 3 ((2 3) (1 1))) => ((3 1) (2 3) (1 1))
  (adjoin-position 2 3 ((2 3) (1 1))) => ((3 2) (2 3) (1 1))
  (adjoin-position 3 3 ((2 3) (1 1))) => ((3 3) (2 3) (1 1))
  (adjoin-position 4 3 ((2 3) (1 1))) => ((3 4) (2 3) (1 1))
  (adjoin-position 5 3 ((2 3) (1 1))) => ((3 5) (2 3) (1 1))
  (adjoin-position 6 3 ((2 3) (1 1))) => ((3 6) (2 3) (1 1))
  (adjoin-position 7 3 ((2 3) (1 1))) => ((3 7) (2 3) (1 1))
  (adjoin-position 8 3 ((2 3) (1 1))) => ((3 8) (2 3) (1 1))

然后对这些进行过滤,给出扩展第一个 2 列解决方案的 3 列问题的四个解决方案。

First 4 (of 140) Solutions to the 3-Column problem:
===================================================
                                      
positions:                            
  ((3 5) (2 3) (1 1))                 
  ((3 6) (2 3) (1 1))                 
  ((3 7) (2 3) (1 1))                 
  ((3 8) (2 3) (1 1))                 
                                      
   _ _ _     _ _ _     _ _ _     _ _ _
  |_|_|_    |_|_|_    |_|_|_    |_|_|Q
  |_|_|_    |_|_|_    |_|_|Q    |_|_|_
  |_|_|_    |_|_|Q    |_|_|_    |_|_|_
  |_|_|Q    |_|_|_    |_|_|_    |_|_|_
  |_|_|_    |_|_|_    |_|_|_    |_|_|_
  |_|Q|_    |_|Q|_    |_|Q|_    |_|Q|_
  |_|_|_    |_|_|_    |_|_|_    |_|_|_
  |Q|_|_    |Q|_|_    |Q|_|_    |Q|_|_

正如你所看到的,我的邻接位置与你的完全不同,它接受一个列表列表并返回一个更长的列表列表(棋盘上皇后的坐标)。

我发现很难理解我自己的解决方案,目前我正在努力理解你的解决方案是如何工作的。如果我的描述的图片/算法部分与您的解决方案相匹配,并且只是位置的表示/实现不同,那么如果您能描述您如何表示这些位置可能会很有用。例如,您希望看到什么而不是:

  (adjoin-position 1 3 ((2 3) (1 1))) => ((3 1) (2 3) (1 1))
  (adjoin-position 2 3 ((2 3) (1 1))) => ((3 2) (2 3) (1 1))
  (adjoin-position 3 3 ((2 3) (1 1))) => ((3 3) (2 3) (1 1))
  ...
,

问题是找bug,问题差点就成功了。在问题中,我们得到了:

它的 old-solutions 参数似乎被传递为零?

解释这一点以及所讨论的错误并不困难。确定的问题:

暗示该错误在以下代码块中,但我没有看到。

(lambda (rest-of-queens)
 (map (lambda (new-row)
        (adjoin-position new-row k rest-of-queens))
      (enumerate-interval 1 board-size)))

要了解出了什么问题,我们需要查看 rest-of-queens 参数。相关部分是 rest-of-queens 将成为我们怀疑的 old-solutions 参数。从引用的这段代码向上看一级,我们发现传递给 rest-of-queens 的值是列表 (queen-cols (- k 1)) 中的条目(它通过对 flatmap 的调用传递,它是map)。这就是我们有错误的地方。该问题怀疑对 adjoin-position 的调用是 (adjoin-position 1 1 (list nil)),其中 (list nil) 将来自对 (queen-cols (- k 1)) 的调用,最终到达 {​​{1}} 案例。虽然 = n 0 将返回 queen-cols 并将其传递给 (list nil) 是正确的,但错误忽略了一个事实,因为 flatmap 是一个 flatmap,传递给 map 的参数是 adjoin-position 的元素,即所讨论的调用将 (list nil) 作为其最终参数,而不是 nil。正因为如此,(list nil) 只使用它的 adjoin-position 分支,使其几乎没有用处。