Stream collect rework added by Zipheir on Wed Jul 25 03:46:12 2018

;; Most of this is stolen from SICP
(define the-empty-stream '())
(define (stream-null? s) (null? s))
(define (stream-car s) (car s))
(define (stream-cdr s) (force (cdr s)))

(define-syntax cons-stream
  (syntax-rules ()
    ((cons-stream a d)
     (cons a (delay d)))))

(define (stream-filter pred stream)
  (cond ((stream-null? stream) the-empty-stream)
        ((pred (stream-car stream))
         (cons-stream (stream-car stream)
                      (stream-filter pred
                                     (stream-cdr stream))))
        (else (stream-filter pred (stream-cdr stream)))))

(define (stream-map proc s)
  (if (stream-null? s)
      the-empty-stream
      (cons-stream (proc (stream-car s))
                   (stream-map proc (stream-cdr s)))))

(define (adjoin-streams s1 s2)
  (cond ((stream-null? s1) s2)
        ((stream-null? s2) s1)
        (else
         (cons-stream (stream-car s1)
                      (adjoin-streams s2 (stream-cdr s1))))))

(define (flatten ss)
  (if (stream-null? ss)
      the-empty-stream
      (adjoin-streams (stream-car ss)
                      (flatten (stream-cdr ss)))))

(define (flatmap f s)
  (flatten (stream-map f s)))

(define (stream-enumerate-interval low high)
  (if (> low high)
      the-empty-stream
      (cons-stream
       low
       (stream-enumerate-interval (+ low 1) high))))

;; macro by siraben
(define-syntax collect
  (syntax-rules ()
    ;; Just one iterator.  Simple case.

    ;; The bound variable could occur free in the collector and filter.
    ((_ collector ((bound-var1 iterator1)) filter)
     (stream-map (lambda (bound-var1)  collector)
                 (stream-filter (lambda (bound-var1) filter)
                                iterator1)))

    ((_ collector
        ((bound-var1 iterator1)
         (bound-var2 iterator2) ...)
        filter)
     (flatmap
      (lambda (bound-var1)
        ;; Make the first bound variable available to the rest of the
        ;; body (because it could occur free in the second iterator.)
        (collect collector
                 ((bound-var2 iterator2) ...)
                 filter))
      iterator1))))

;; example usage

(define (evens n)
  (collect a
           ((a (range 1 n)))
           (even? a)))

(define (prime-sum-pairs n)
  (collect
   (list i j (+ i j))
   ((i (range 1 n))
    (j (range 1 (- i 1))))
   (prime? (+ i j))))