# Conjunction and Disjunction of Streams

**URL:** <https://racket.discourse.group/t/conjunction-and-disjunction-of-streams/3352>\
**Category:** Questions & Answers\
**Created:** [November 23, 2024, 2:57pm UTC](https://racket.discourse.group/t/conjunction-and-disjunction-of-streams/3352 "2024-11-23T14:57:31Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![bakgatviooldoos](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/bakgatviooldoos/32/1381_2.png) [@bakgatviooldoos](https://racket.discourse.group/u/bakgatviooldoos)\
**Post date:** [November 23, 2024, 2:57pm UTC](https://racket.discourse.group/t/conjunction-and-disjunction-of-streams/3352/1 "2024-11-23T14:57:31Z")

</div>

Hi, Racket Discourse.

I am reimplementing some logic for my iterations over multiset-relations in terms of streams for to see what there is to see.

The two basic operations to which I need access, are the conjunction (product) and disjunction (interleaving) of streams. The latter is simple enough, being an oft shown example, but I am wondering if I am overthinking the product.

Also, is it okay to slap the `stream-lazy` expressions in there as I do and call it a day?

My previous implementation uses generators, so I had very little concern for actually "sequencing" things beyond `yield`-ing at the appropriate time.

```scheme
#lang racket

(require
  "shortnames.rkt")

(provide
  stream-disj
  stream-conj)

(define (interleave x y)
  (stream-lazy
   (if (stream-empty? x)
       y
       (stream-cons
        (stream-first x) (interleave y (stream-rest x))))))

(define (stream-disj streams)
  (foldl interleave empty-stream (reverse streams)))

(define-unique stnl)
; just my own nonsense producing:
; stnl : (gensym)
; stnl? : == stnl

(define (cycle-stream stm)
  (stream-lazy
   (stream-cons stnl (stream-append stm (cycle-stream stm)))))

(define cycle-front-to-back
  (match-lambda
    [(list inf ... fin) `(,@(map cycle-stream inf) ,fin)]))

(define (stream-conj streams #:combine [combine values])
  (if (null? streams)
      empty-stream
      (let loop ([prev streams]
                 [stms (cycle-front-to-back streams)])
        (stream-lazy
         (for/fold ([tick #true]
                    [prod (list)]
                    [stms (list)]
                    #:result
                    (cond
                      [(not prod) empty-stream]
                      [else
                       (define curr (reverse prod))
                       (stream-cons
                        (combine curr) (loop curr (reverse stms)))]))
                   ([stm (in-list stms)]
                    [old (in-list prev)]
                    #:break (not prod))
           (cond
             [(not tick)
              (values tick (cons old prod) (cons stm stms))]

             [(stream-empty? stm)
              (values tick #false stms)]
                    
             [(stnl? (stream-first stm))
              (define stm′ (stream-rest stm))
              (define new (stream-first stm′))
              (values
               tick (cons new prod) (cons (stream-rest stm′) stms))]

             [else
              (define new (stream-first stm))
              (values
               (not tick) (cons new prod) (cons (stream-rest stm) stms))]))))))

(stream->list
 (stream-disj
  (list (stream 1 2 3)
        (stream 4 5)
        (stream 6 7 8))))

(stream->list
 (stream-conj
  (list (stream 1 2 3)
        (stream 4 5)
        (stream 6 7 8))))

;=>
#|
'(1 4 2 6 3 5 7 8)
'((1 4 6)
  (2 4 6)
  (3 4 6)
  (1 5 6)
  (2 5 6)
  (3 5 6)
  (1 4 7)
  (2 4 7)
  (3 4 7)
  (1 5 7)
  (2 5 7)
  (3 5 7)
  (1 4 8)
  (2 4 8)
  (3 4 8)
  (1 5 8)
  (2 5 8)
  (3 5 8))
|#

```

* * *

Edit: like the `stream-lazy` I added now, is that strictly necessary? I would think so, but am unsure of the "extent" to which it applies.

* * *

Edit: best of three 🤞

* * *

Edit: I didn't know you could `match` on streams! Go figure.

```scheme
; much easier to read at a glance
(match stm
  [_ #:when (not tick)
   (values tick (cons old prod) (cons stm stms))]

  [(stream)
   (values tick #false stms)]

  [(stream* (? tock?) new rest) ; I renamed `stnl` to `tock` because it's cute
   (values tick (cons new prod) (cons rest stms))]

  [(stream* new rest)
   (values (not tick) (cons new prod) (cons rest stms))])

; than this, I would hazard
(cond
  [(not tick)
   (values tick (cons old prod) (cons stm stms))]
             
  [(stream-empty? stm)
   (values tick #false stms)]
             
  [(tock? (stream-first stm))
   (define stm′ (stream-rest stm))
   (define new (stream-first stm′))
   (values tick (cons new prod) (cons (stream-rest stm′) stms))]
             
  [else
   (define new (stream-first stm))
   (values (not tick) (cons new prod) (cons (stream-rest stm) stms))])

```
