;; sequences.scm 
;;
;; working up to the eight queens exercise, 2.42 in SICP
;;

(define (accumulate op initial sequence)
  ;; reduce sequence by combining elements with op :
  ;; (accumulate + 0 '(1 2 3 4)) is (+ 1 (+ 2 (+ 3 (4 0))))
  ;; i.e. "fold-right"
  (if (null? sequence)
         initial
         (op (car sequence)
             (accumulate op
                         initial
                         (cdr sequence)))))

(define (enumerate-interval low high)
  ;; generate integer sequence (low low+1 low+2 ... high)
  (if (> low high)
      nil
      (cons low
            (enumerate-interval
             (+ low 1)
             high))))

(define (map proc items)
  ;; generate a list new-items by applying proc to each item
  ;; ((proc items1) (proc items2) ...)
  (if (null? items)
      nil
      (cons (proc (car items))
            (map proc (cdr items)))))

(define (flatmap proc seq)
  ;; like map but combine the elements with append rather than cons,
  ;; so that even if seq is a list-of-lists like (1 2 (3 4)),
  ;; flatten out the result to (1 2 3 4)
  (accumulate append nil (map proc seq)))

;; Compare these two :

(define (grid n) (map (λ (i) 
                        (map (λ (j) (list i j))
                             (enumerate-interval 1 n)))
                        (enumerate-interval 1 n)))

(printf "(grid 3) is ~a\n" (grid 3))

(define (pairs n) (flatmap (λ (i) 
                             (map (λ (j) (list i j))
                                  (enumerate-interval 1 (- i 1))))
                           (enumerate-interval 1 n)))

(printf "(pairs 4) is ~a\n" (pairs 4))

(define (filter predicate? items)
  ;; generate new-items containing only those items
  ;; for which (predicate? item) is true
  (cond ((null? items) nil)
        ((predicate? (car items))
         (cons (car items)
               (filter predicate? (cdr items))))
        (else  (filter predicate? (cdr items)))))