2

I've seen the other answers on zipping functions in Racket but they are first of all not quite right (a zip should only zip up to the shortest sequence provided so that you can zip with infinite streams) and most importantly not varadic so you can only zip two streams at a time.

I have figured out this far

 (define (zip a-sequence b-sequence) (for/stream ([a a-sequence]
                                                  [b b-sequence])
                                       (list a b)))

which does work correctly

 (stream->list (zip '(a b c) (in-naturals)))
  => '((a 0) (b 1) (c 2))

but is not varadic. I know I can define it to be varadic with define (zip . sequences) but I have no idea how to build the for/stream form if I do.

Does this have to be a macro to be doable?

Will Ness
  • 70,110
  • 9
  • 98
  • 181
George Mauer
  • 117,483
  • 131
  • 382
  • 612

2 Answers2

4

Would this work for you?

#lang racket

(define (my-zip . xs)
  (match xs
    [(list x) (for/stream ([e x]) (list e))]
    [(list x xs ...)
     (for/stream ([e x] [e* (apply my-zip xs)])
       (cons e e*))]))

(stream->list
 (my-zip (in-naturals) '(a b c) '(1 2 3 4 5 6)))
;;=> '((0 a 1) (1 b 2) (2 c 3))
Sorawee Porncharoenwase
  • 6,305
  • 1
  • 14
  • 28
  • oh that's fun... yeah that looks like it would work...just so I'm sure what I'm looking at there: The first `match` clause will match if there is exactly one list passed in which case you're outputting a stream of lists with one item for each in the stream. The second will match if there is more than one...then it will take the first stream `x` and for each item in it it will apply `zip` to the remainder.... but then what is the `e*` part? – George Mauer Dec 08 '19 at 03:49
  • Also, while I can see this works I'm surprised that matching on `(list x)` works for things that are not lists but infinite streams – George Mauer Dec 08 '19 at 03:49
  • Q: while I can see this works I'm surprised that matching on (list x) works for things that are not lists but infinite streams A: No, we do match on a list. If we were to match on an infinite stream, there would be an error. Notice that `xs` is not an argument of `my-zip`, but is an _argument list_. Q: what is the e* part A: I think this might help: `(my-zip (in-naturals) '(a b c) '(1 2 3 4 5 6)))` = `(for/stream ([e (in-naturals)] [e* (stream (list 'a 1) (list 'b 2) (list 'c 3)]) (cons e e*))` – Sorawee Porncharoenwase Dec 08 '19 at 04:15
2

A common implementation of zip is:

(require data/collection) ; for a `map` function that works with streams

(define (zip . xs)
  (apply map list xs))

... and if you prefer the point-free style, this can be simply:

(require racket/function)
(define zip (curry map list))

These do have the limitation that they require all input sequences to have the same length (including infinite), but since they are computed lazily, they would work in any case until one of the sequences runs out, at which point an error would be raised.

(zip (cycle '(a b c))
     (naturals)
     (cycle '(1 2 3))) ; => '((a 0 1) (b 1 2) (c 2 3) (a 3 1) ...

[Answer updated to reflect comments]

mindthief
  • 12,755
  • 14
  • 57
  • 61
  • 1
    doesn't this produce a stream of streams, whereas the OP wanted a stream of lists? We have only finite number of arguments anyway so `list` should be perfectly fine. With " `map` that works with streams" your very first snippet is all that's needed, is it not? Am I missing something? – Will Ness Dec 13 '19 at 08:44
  • you're right, @WillNess! If there is a finite number of possibly-infinite input sequences then the first snippet (along with the lazy map) is all that's needed. I may have misunderstood the question to imply that OP wanted to support an infinite number of zipped sequences as well. But now that I'm thinking about it, I'm not sure Racket would be able to supply the `xs` args lazily -- I assume it is a list being provided to `my-stream` rather than a stream. In which case, the latter snippet may not work for infinite streams of infinite streams after all... – mindthief Dec 13 '19 at 09:03