AnhNg
AnhNg

Reputation: 199

Stream of all pairs of elements of infinite stream

How to define a procedure return all pairs of elements in an infinite stream s?

s = {1,2,3,4,5,6,...}
=> {(2,1), (3,2), (3,1), (4,3), (4,2), (4,1), ......}

Here is my code, however it didn't work like a stream, it keep running infinitely and ran out of memory.

(define (stream-pairs s)
    (define (iter s save)
            (stream-append (stream-map (lambda (x) (stream-cons (stream-first s) x))
                                       save)
                           (iter (stream-rest s) (stream-cons save (stream-first s)))))
    (iter s empty-stream))

(define A (stream-cons 1 (scale-stream 2 A)))

(define C (stream-pairs A))

A = {1,2,4,8,16,......}

Upvotes: 1

Views: 1859

Answers (3)

Will Ness
Will Ness

Reputation: 71065

Turns out, Racket's stream-append does not delay its last (at least) argument, so iter calls stream-append which calls iter ... thus the loop.

One way is to reimplement the stream-append fused with the stream-map as used here, as a simple recursive function. That way the tail will be properly under the guard of the delaying stream-cons.

Another is to take a stream-rest1 of a fake stream-cons:

(require racket/stream)

(define (stream-pairs s)
    (define (iter s save)
            (stream-append (stream-map (lambda (x) (list (stream-first s) x))
                                       save)       ;^^^^
                           (stream-rest
                            (stream-cons 'fake  ;<<-----------------
                                 (iter (stream-rest s) 
                                       (stream-cons (stream-first s) save))))))
    (iter s empty-stream))

(define A (stream-cons 1 (stream-map add1 A))) ; easier to follow

(define C (stream-pairs A))

Also, there was another error in your code where stream-cons was used instead of plain list, to pair up the elements of save with a current element of the input stream. Now we have

 > (for ((i (in-range 0 12))) (display (stream-ref C i)))
(2 1)(3 2)(3 1)(4 3)(4 2)(4 1)(5 4)(5 3)(5 2)(5 1)(6 5)(6 4)


1 cf.,

> (stream-rest (stream-cons 1 (/ 1 0)))
#<stream>

Upvotes: 1

Majora320
Majora320

Reputation: 1351

I'm unclear as what you mean by 'pairs of a stream', but assuming you want something like this:

> (stream-subset-pairs (in-naturals) 5) 
'((0 . 1) (1 . 2) (2 . 3) (3 . 4))

With that as the expected output, here is a solution:

(define (stream-subset-pairs stream i)
  (define subset ; collect all elements up to i by iterating over the list and all #'s in sequence
    (for/list ([element stream]
               [position (in-naturals)]
               #:break (= position i)) ; break when we have reached the # of elements we want to collect
      element))
  (let-values ([(ignore result) ; wrapper because we want for/fold to produce one result (last-element is just a state variable)
                (for/fold ([last-element null]
                           [result empty])
                          ([element subset]) ; iterate through the subset
                  (values element (if (null? last-element) ; if we are on the first loop
                                      result ; ignore it
                                      (cons (last-element element) result))))]) ; else add a pair of the last result and this result
    result))

Upvotes: 0

C. K. Young
C. K. Young

Reputation: 223023

Since the question specified that the given stream is infinite:

(define (stream-pairs strm)
  (stream-cons (cons (stream-first strm)
                     (stream-first (stream-rest strm)))
               (stream-pairs (stream-rest (stream-rest strm)))))

(If the stream is not infinite, you need to add an emptiness guard to the front of the function.)

Note: If you're using SRFI 41 streams instead of Racket streams, change define to define-stream.

Upvotes: 0

Related Questions