我被扩展的exercise 28.2 of How to Design Programs卡住了。我使用true或false值的向量来表示棋盘,而不是使用列表。这就是我得到的不起作用的东西:
#lang Scheme
(define-struct posn (i j))
;takes in a position in i, j form and a board and
; returns a natural number that represents the position in index form
;example for board xxx
; xxx
; xxx
;(0, 1) -> 1
;(2, 1) -> 7
(define (board-ref a-posn a-board)
(+ (* (sqrt (vector-length a-board)) (posn-i a-posn))
(posn-j a-posn)))
;reverse of the above function
;1 -> (0, 1)
;7 -> (2, 1)
(define (get-posn n a-board)
(local ((define board-length (sqrt (vector-length a-board))))
(make-posn (floor (/ n board-length))
(remainder n board-length))))
;determines if posn1 threatens posn2
;true if they are on the same row/column/diagonal
(define (threatened? posn1 posn2)
(cond
((= (posn-i posn1) (posn-i posn2)) #t)
((= (posn-j posn1) (posn-j posn2)) #t)
((= (abs (- (posn-i posn1)
(posn-i posn2)))
(abs (- (posn-j posn1)
(posn-j posn2)))) #t)
(else #f)))
;returns a list of positions that are not threatened or occupied by queens
;basically any position with the value true
(define (get-available-posn a-board)
(local ((define (get-ava index)
(cond
((= index (vector-length a-board)) '())
((vector-ref a-board index)
(cons index (get-ava (add1 index))))
(else (get-ava (add1 index))))))
(get-ava 0)))
;consume a position in the form of a natural number and a board
;returns a board after placing a queen on the position of the board
(define (place n a-board)
(local ((define (foo x)
(cond
((not (board-ref (get-posn x a-board) a-board)) #f)
((threatened? (get-posn x a-board) (get-posn n a-board)) #f)
(else #t))))
(build-vector (vector-length a-board) foo)))
;consume a list of positions in the form of natural numbers, and a board
;returns a list of boards after placing queens on each of the positions
; on the board
(define (place/list alop a-board)
(cond
((empty? alop) '())
(else (cons (place (first alop) a-board)
(place/list (rest alop) a-board)))))
;returns a possible board after placing n queens on a-board
;returns false if impossible
(define (placement n a-board)
(cond
((zero? n) a-board)
(else (local ((define available-posn (get-available-posn a-board)))
(cond
((empty? available-posn) #f)
(else (or (placement (sub1 n)
(place (first available-posn) a-board))
(placement/list (sub1 n)
(place/list (rest available-posn) a-board)))))))))
;returns a possible board after placing n queens on a list of boards
;returns false if all the boards are not valid
(define (placement/list n boards)
(cond
((empty? boards) #f)
((zero? n) (first boards))
((not (boolean? (placement n (first boards)))) (first boards))
(else (placement/list n (rest boards)))))发布于 2010-05-25 04:25:53
这可能不是最快的方案实现,但它相当简洁。我确实是独立想出来的,但我怀疑它是独一无二的。它采用PLT Scheme,因此需要更改一些函数名才能使其在R6RS中运行。解决方案列表和每个解决方案都是用cons构建的,所以它们是颠倒的。最后的反转和映射对所有内容进行了重新排序,并将行添加到解决方案中,以获得漂亮的输出。大多数语言都有一个折叠型函数,请参见:
http://en.wikipedia.org/wiki/Fold_%28higher-order_function%29
#lang scheme/base
(define (N-Queens N)
(define (attacks? delta-row column solution)
(and (not (null? solution))
(or (= delta-row (abs (- column (car solution))))
(attacks? (add1 delta-row) column (cdr solution)))))
(define (next-queen safe-columns solution solutions)
(if (null? safe-columns)
(cons solution solutions)
(let move-queen ((columns safe-columns) (new-solutions solutions))
(if (null? columns) new-solutions
(move-queen
(cdr columns)
(if (attacks? 1 (car columns) solution) new-solutions
(next-queen (remq (car columns) safe-columns)
(cons (car columns) solution)
new-solutions)))))))
(unless (exact-positive-integer? N)
(raise-type-error 'N-Queens "exact-positive-integer" N))
(let ((rows (build-list N (λ (row) (add1 row)))))
(reverse (map (λ (columns) (map cons rows (reverse columns)))
(next-queen (build-list N (λ (i) (add1 i))) null null)))))如果你考虑这个问题,列表实际上是这个问题的自然数据结构。由于每行上只能放置一个皇后,因此需要做的就是将安全或未使用的列的列表传递给下一行的迭代器。这是通过调用cond子句中的remq来完成的,该子句对next-queen进行回溯调用。
foldl函数可以重写为命名let:
(define (next-queen safe-columns solution solutions)
(if (null? safe-columns)
(cons solution solutions)
(let move-queen ((columns safe-columns) (new-solutions solutions))
(if (null? columns) new-solutions
(move-queen这是相当快的,因为它避免了内置到foldl中的参数检查开销。在查看PLT Scheme N-Queens基准测试时,我突然想到了使用隐式行的想法。从1的增量行开始,并随着解决方案的检查而递增,这是相当巧妙的。由于某些原因,在PLT方案中abs是昂贵的,所以有更快的攻击形式吗?
在PLT Scheme中,您必须使用可变列表类型以获得最快的实现。在不返回解决方案的情况下计算解决方案的基准测试可以在不创建任何cons单元格的情况下编写,除了初始列列表。这避免了在N= 17之前收集垃圾,此时gc花费了618毫秒,而程序花了1小时51分钟找到95,815,104个解决方案。
发布于 2010-04-11 16:40:30
这是大约11年前,当我有一个函数式编程课程,我想这是使用麻省理工学院的方案或mzScheme。主要是我们使用的Springer/Friedman文本的修改,它只解决了8个皇后的问题。这个练习是将它推广到N个queens,下面的代码就是这么做的。
;_____________________________________________________
;This function tests to see if the next attempted move (try)
;is legal, given the list that has been constructed thus far
;(if any) - legal-pl (LEGAL PLacement list)
;N.B. - this function is an EXACT copy of the one from
;Springer and Friedman
(define legal?
(lambda (try legal-pl)
(letrec
((good?
(lambda (new-pl up down)
(cond
((null? new-pl) #t)
(else (let ((next-pos (car new-pl)))
(and
(not (= next-pos try))
(not (= next-pos up))
(not (= next-pos down))
(good? (cdr new-pl)
(add1 up)
(sub1 down)))))))))
(good? legal-pl (add1 try) (sub1 try)))))
;_____________________________________________________
;This function tests the length of the solution to
;see if we need to continue "cons"ing on more terms
;or not given to the specified board size.
;
;I modified this function so that it could test the
;validity of any solution for a given boardsize.
(define solution?
(lambda (legal-pl boardsize)
(= (length legal-pl) boardsize)))
;_____________________________________________________
;I had to modify this function so that it was passed
;the boardsize in its call, but other than that (and
;simply replacing "fresh-start" with boardsize), just
;about no changes were made. This function simply
;generates a solution.
(define build-solution
(lambda (legal-pl boardsize)
(cond
((solution? legal-pl boardsize) legal-pl)
(else (forward boardsize legal-pl boardsize)))))
;_____________________________________________________
;This function dictates how the next solution will be
;chosen, as it is only called when the last solution
;was proven to be legal, and we are ready to try a new
;placement.
;
;I had to modify this function to include the boardsize
;as well, since it invokes "build-solution".
(define forward
(lambda (try legal-pl boardsize)
(cond
((zero? try) (backtrack legal-pl boardsize))
((legal? try legal-pl) (build-solution (cons try legal-pl) boardsize))
(else (forward (sub1 try) legal-pl boardsize)))))
;_____________________________________________________
;This function is used when the last move is found to
;be unhelpful (although valid) - instead it tries another
;one until it finds a new solution.
;
;Again, I had to modify this function to include boardsize
;since it calls "forward", which has boardsize as a
;parameter due to the "build-solution" call within it
(define backtrack
(lambda (legal-pl boardsize)
(cond
((null? legal-pl) '())
(else (forward (sub1 (car legal-pl)) (cdr legal-pl) boardsize)))))
;_____________________________________________________
;This is pretty much the same function as the one in the book
;with just my minor "boardsize" tweaks, since build-solution
;is called.
(define build-all-solutions
(lambda (boardsize)
(letrec
((loop (lambda (sol)
(cond
((null? sol) '())
(else (cons sol (loop (backtrack sol boardsize))))))))
(loop (build-solution '() boardsize)))))
;_____________________________________________________
;This function I made up entirely myself, and I only
;made it really to satisfy the syntactical limitations
;of the laboratory instructions. This makes it so that
;the input of "(queens 4)" will return a list of the
;two possible configurations that are valid solutions,
;even though my modifiend functions would return the same
;value by simply inputting "(build-all-solutions 4)".
(define queens
(lambda (n)
(build-all-solutions n)))发布于 2011-04-23 05:59:54
观看大师(Hal Ableson)这样做:
http://www.youtube.com/watch?v=skd-nyVyzBQ
https://stackoverflow.com/questions/2595132
复制相似问题