I'm iterating through a list, building up state as I go, and occasionally when I encounter a certain sentinel, I return a result. If I was doing this in Python, I would lazily yield
the results, tracking state in the function's local scope as I go:
# this is simplified for illustration
def yielder(input_list):
state = 0
for item in input_list:
if item = 'SENTINEL':
yield state * 2
state = 0
else:
state += item
yielder([1, 5, 2, 5, 'SENTINEL', 4, 6, 7]) # [26, 34]
My first implementation uses reduce
, but that's not as good as yield
because:
iterate
could be used to mitigate the latter, but i don't actually want to return something for every input item, so it would require more munging.
What's an idiomatic way to do this in Clojure?
You can build this yourself using lazy-seq as you mention or you could use partition
and reduce
to split the problem into phases then thread them together. I'll use the thread-last macro to show each step on it's own:
user> (->> [1, 5, 2, 5, :SENTINEL, 4, 6, 7] ;; start with data
(partition-by #(= :SENTINEL %)) ;; ((1 5 2 5) (:SENTINEL) (4 6 7))
(take-nth 2) ;; ((1 5 2 5) (4 6 7))
(map #(* 2 (reduce + %)))) ;; the map here keeps it lazy
(26 34)
and here it is usin lazy-seq directly:
user> (defn x [items]
(when (seq items)
(lazy-seq (cons (* 2 (reduce + (take-while #(not= :SENTINEL %) items)))
(x (rest (drop-while #(not= :SENTINEL %) items)))))))
#'user/x
user> (x [1, 5, 2, 5, :SENTINEL, 4, 6, 7])
(26 34)