So I found myself needing to group a sequence by two different ways of grouping it without iterating through it multiple times.
;; (in-labeled-groups `((a . ,f) (b . ,g)) seq) returns a sequence that's the
;; interleaving of (in-map (curry cons 'a) (in-groups f seq)) and (in-map
;; (curry cons 'b) (in-groups g seq)), except that seq is only consumed once.
;; (Mapping and grouping separately would try to consume the sequence twice,
;; which doesn't actually work.) This can be generalized to any number of
;; labeled predicates, of course.
;;
;; (sequence->list (in-labeled-groups `((eq . ,equal?) (lte . ,<=)) '(1 2 2 2 3 2 2 3 4 5)))
;; ((eq 1)
;; (eq 2 2 2)
;; (eq 3)
;; (lte 1 2 2 2 3)
;; (eq 2 2)
;; (eq 3)
;; (eq 4)
;; (eq 5)
;; (lte 2 2 3 4 5))
(define (in-labeled-groups binary-pred-assocs seq)
(in-once (let-values ([(has-next? next!) (sequence-generate seq)])
(when (has-next?)
(let loop ([current-lists-reversed (let ([x (next!)])
(make-list (length binary-pred-assocs) (list x)))])
(if (has-next?)
(let ([next (next!)])
(loop (for/list ([reversed-group current-lists-reversed]
[labeled-binary-pred binary-pred-assocs])
(if ((cdr labeled-binary-pred) (car reversed-group) next)
(cons next reversed-group)
(begin
(yield (cons (car labeled-binary-pred) (reverse reversed-group)))
(list next))))))
(for ([reversed-group current-lists-reversed]
[labeled-binary-pred binary-pred-assocs])
(yield (cons (car labeled-binary-pred) (reverse reversed-group))))))))))
So that was very ugly.
One nice thing about this function is that it’s an example of one where I’d feel pretty uncomfortable writing it in Haskell. What would its type signature be? How convenient would it be to use? This implementation here scales well with respect to the number of ways in which I’m grouping the sequence, since it’s just another label.
One question is: how am I going to like splitting the sequence of groups up later? I’m starting to feel the call of reactive programming. How good is PLT Scheme at having lots of tiny threads? I might need to look into that. I’m having one of those moments where you recognize threads as a tool for making cleaner code, instead of as a tool for doing two things at the same time. But this is a moment of insanity.