天天看點

scheme解決約瑟夫環問題(續)

sicp的習題3.22,也就是以消息傳遞的風格重新實作隊列,我的解答如下:

(define (make-queue)

  (let ((front-ptr '())

        (rear-ptr '()))

  (define (set-front-ptr! ptr) (set! front-ptr ptr))

  (define (set-rear-ptr! ptr) (set! rear-ptr ptr))

  (define (empty-queue?) (null? front-ptr))

  (define (front-queue)

    (if (empty-queue?)

        (error "front called with an empty queue")

        (car front-ptr)))

  (define (insert-queue! item)

    (let ((new-pair (cons item '())))

      (cond ((empty-queue?)

              (set-front-ptr! new-pair)

              (set-rear-ptr! new-pair))

            (else

               (set-cdr! rear-ptr new-pair)

               (set-rear-ptr! new-pair)))))

  (define (delete-queue!)

             (error "delete! called with an empty queue" queue))

               (set-front-ptr! (cdr front-ptr)))))

  (define (dispatch m)

    (cond ((eq? m 'front-queue) (front-queue))

          ((eq? m 'empty-queue?) (empty-queue?))

          ((eq? m 'insert-queue!) insert-queue!)

          ((eq? m 'delete-queue!) delete-queue!)

          (else

             (error "unknow method" m))))

    dispatch))

(define (front-queue z) (z 'front-queue))

(define (empty-queue? z) (z 'empty-queue?))

(define (insert-queue! z item) ((z 'insert-queue!) item))

(define (delete-queue! z) ((z 'delete-queue!)))

    由此,我才知道自己竟然一直沒有想到,scheme完全可以模拟單向循環連結清單,整整第三章都在講引入指派帶來的影響,而我卻視而不見。在引入了改變函數後,資料對象已經具有oo的性質,模拟連結清單、隊列、table都變的易如反掌。首先,模拟節點對象,節點是一個序對,包括目前節點編号和下一個節點:

(define (make-node n next) (cons n next))

(define (set-next-node! node next) (set-cdr! node next))

(define (set-node-number! node n) (set-car! node n))

(define (get-number node) (car node))

(define (get-next-node node) (cdr node))

    有了節點,實作了下單向循環連結清單:

(define (make-cycle-list n)

  (let ((head (make-node 1 '())))

    (define (make-list current i)

      (let ((next-node (make-node (+ i 1) '())))

        (cond ((= i n) current)

              (else

                (set-next-node! current next-node)

                (make-list next-node (+ i 1))))))

    (set-next-node! (make-list head 1) head) 

    head))

    make-cycle-list生成一個有n個元素的環形連結清單,比如(make-cycle-list 8)的結果如下

#0=(1 2 3 4 5 6 7 8 . #0#)

    drscheme形象地展示了這是一個循環的連結清單。那麼約瑟夫環的問題就簡單了:

(define (josephus-cycle n m)

  (let ((head (make-cycle-list n)))

    (define (josephus-iter prev current i)

      (let ((next-node (get-next-node current)))

       (cond ((eq? next-node current) (get-number current))

             ((= 1 i)

              (set-next-node! prev next-node)

              (josephus-iter prev next-node m))

             (else

               (josephus-iter current next-node (- i 1))))))

    (josephus-iter head head m)))

    從head節點開始計數,每到m,就将目前節點删除(通過将前一個節點的next-node設定為current的下一個節點),最後剩下的節點的編号就是答案。

文章轉自莊周夢蝶  ,原文釋出時間 2008-04-16