Exercise 4.50.  Implement a new special form ramb that is like amb 
except that it searches alternatives in a random order, rather than from 
left to right. Show how this can help with Alyssa's problem in exercise 
4.49.

————————————————————————————————————————————————————————————————————————

;; from the book we have:

(define (analyze-amb exp)
  (let ((cprocs (map analyze (amb-choices exp))))
    (lambda (env succeed fail)
      (define (try-next choices)
        (if (null? choices)
            (fail)
            ((car choices) env
                           succeed
                           (lambda ()
                             (try-next (cdr choices))))))
      (try-next cprocs))))

;; analyze-ramb is very similar, but we re-order the choices randomly 
;; each time the expression is evaluated (not just once when it is 
;; analyzed)

(define (analyze-ramb exp)
  (let ((cprocs (map analyze (ramb-choices exp))))
    (lambda (env succeed fail)
      (define (try-next choices)
        (if (null? choices)
            (fail)
            ((car choices) env
                           succeed
                           (lambda ()
                             (try-next (cdr choices))))))
      (try-next (random-permutation-of cprocs)))))

; this is O(n²), and I'm sure this can be improved, but in most programs n will be quite small anyway
(define (random-permutation-of items)
  (define (permute items length)
    (if (= 0 length)
        '()
        (let ((selected-index (random length)))
          (let ((bubbled (bubble items selected-index)))
            (cons (car bubbled) (permute (cdr bubbled) (- length 1)))))))
  (permute items (length items)))

; move the index'th item to the front of the list, leaving the rest of the items in some unspecified order
(define (bubble items index)
  (if (= 0 index)
      items
      (let ((rest (bubble (cdr items) (- index 1))))
        (cons (car rest) (cons (car items) (cdr rest))))))
