代码框架来自sicp 练习2.42。算是作业吧。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
(define (enumerate-interval l r)
  (if (= l r)
      (list l)
      (cons l (enumerate-interval (+ l 1) r))))

;(enumerate-interval 1 10)

(define (contains? e pl)
  (if (null? pl)
      #f
      (or (eq? e (car pl))
          (contains? e (cdr pl)))))

;检查形如(2 1 4 3 2)这样的positions list中有没有重复元素,简单办法,hash会是更好的办法。
(define (no-repeat? positions) 
  (if (null? positions)
      #t
      (and (not (contains? (car positions) (cdr positions)))
           (no-repeat? (cdr positions)))))

;(no-repeat? '(1 2))
;(no-repeat? '(1 2 1))

;按理说应该检查一下rest-of-queens是不是length = (- k 1)...
;使用cons可以想象成把所有列往右移一列再把新的添到第一列,这样不影响safe?的判断,简单。
(define (addjoin-position new-row k rest-of-queens)
  (cons new-row rest-of-queens))

(define (flatmap proc seq)
  (foldr append () (map proc seq)))

(define (safe? k positions)
  ;两个点在对角线上,也就是满足同一个方程x+y=n或x-y=n
  ;所以要判断两个点是不是在同一条“左下至右上对角线”上,
  ;只需要算出两个x-y,判断是否相等即可。左上至右下同理。
  (define (px-y pl)
    (map + positions (enumerate-interval 1 k)))
  (define (px+y pl)
    (map - positions (enumerate-interval 1 k)))  
  (and
   (no-repeat? positions);不在同一行
   (no-repeat? (px-y positions));不在同一条“左下至右上对角线”上
   (no-repeat? (px+y positions));不在同一条“左上至右下对角线”上
   ))

;(safe? 1 '(1))
;(safe? 2 '(1 2))
;(safe? 3 '(1 2 3))
;(safe? 4 '(3 1 4 2))
;(safe? 4 '(2 4 1 3))

;层次遍历状态空间树,剪掉每一层中不合适的分支后再扩展到下一层。
(define (queens board-size)
  (define empty-board ())
  (define (queen-cols k)
    ;一个布局表示成一个list,形如(2 4 1 3),表示第一列为2,第二列为2,第三列为1...
    ;queens-cols 返回((1,2,3)(2,1,3)(3,2,1))这样的布局组成的list
    (if (= k 0)
        (list empty-board)
        (filter 
         (lambda (positions) (safe? k positions))
         (flatmap 
          (lambda (reat-of-queens)
            (map (lambda (new-row)
                   (addjoin-position new-row k reat-of-queens))
                 (enumerate-interval 1 board-size)))
          (queen-cols (- k 1))))))
  (queen-cols board-size))

;(queens 4)
(queens 8)