Useful generators

iota

Similar to range but returns a generator instead of an array.

(defn iota [n]
  (generate [i :range [0 n]] i))

(values (iota 3)) # => @[0 1 2]

zip

Given multiple generators, zip returns a generator that yields arrays with one value from each input generator. It stops when on of the inputs is out of elements.

(defn zip
  [& rest]
  (fiber/new
    (fn []
      (var k (map next rest))
      (while (every? k)
        (yield (seq [i :range [0 (length rest)]]
                 (in (in rest i) (in k i))))
        (set k (seq [i :range [0 (length rest)]]
                 (next (in rest i) (in k i))))))))

(values (zip ["a" "b" "c"] (iota 100))) # => @[0 1 2] @[@["a" 0] @["b" 1] @["c" 2]]

# Note: `map` can be used in a similar way, as long as one of the arguments is
# not an infinite generator
(map tuple ["a" "b" "c"] (iota 100)) # => @[("a" 0) ("b" 1) ("c" 2)]

window

(defn window [n ds]
  (fiber-fn :yi []
            (var k (next ds))
            (var win @[])
            (loop [:repeat n]
              (array/push win (in ds k))
              (set k (next ds k)))
            (yield win)
            (while (not= k nil)
              (set win (array/slice win 1))
              (array/push win (in ds k))
              (yield win)
              (set k (next ds k)))
            win))

(values (window 3 (iota 5))) # => @[@[0 1 2] @[1 2 3] @[2 3 4]]

chain

(defn chain
  "Chain multiple fibers or data structures together"
  [& fbrs]
 (coro 
   (loop [fb :in fbrs]
    (var k (next fb))
    (while (not= k nil) 
      (yield (in fb k))
      (set k (next fb k))))))