Мне нравится ответ преобразователя с состоянием , но заметил, что вопрос не говорит о том, что должно быть, если элемент start найден, но не stop элемент найден.Если подгруппа остается открытой , преобразователь усекает входную последовательность, что может быть неожиданным / нежелательным.Рассмотрим пример с удаленными элементами stop :
(into [] (subgroups #{:b} #{:d}) [:a :b :c :e :f :g :h :b :x])
=> [:a] ;; drops inputs from before (last) subgroup opens
У преобразователей есть завершающая арность , которую можно использовать для сброса любой открытой подгруппы в этом случае:
Завершение (арность 1) - некоторые процессы не заканчиваются, но для тех, которые делают (например, преобразование), арность завершения используется для получения окончательного значения и / или состояния сброса.Эта арность должна вызывать арфию завершения xf ровно один раз.
Единственная разница в этом примере и оригинальном примере преобразователя состоит в том, что завершает арность:
(defn subgroups-all [start? stop?]
(let [subgroup (volatile! nil)]
(fn [rf]
(fn
([] (rf))
([result] ;; completing arity flushes open subgroup
(let [sg @subgroup]
(if (seq sg)
(do (vreset! subgroup nil)
(rf result sg))
(rf result))))
([result item]
(let [sg @subgroup]
(cond
(and (seq sg) (stop? item))
(do (vreset! subgroup nil)
(rf result (conj sg item)))
(seq sg)
(do (vswap! subgroup conj item)
result)
(start? item)
(do (vreset! subgroup [item])
result)
:else (rf result item))))))))
Затем свисающие, открытые группы будут сброшены:
(into [] (subgroups-all #{:b} #{:d}) [:a :b :c :d :e :f :g :h :b :x])
=> [:a [:b :c :d] :e :f :g :h [:b :x]]
(into [] (subgroups-all #{:b} #{:d}) [:a :b :c :e :f :g :h :b :x])
=> [:a [:b :c :e :f :g :h :b :x]]
Обратите внимание, что в последнем примере вложенные запуск / открытие не приводят к вложенным группировкам, что заставило меня задуматься о другом решении ...
Вложенные группы и застежки-молнии
Когда я подумала об этом в более общем смысле как о "разворачивании" последовательности, мне в голову пришли молнии:
(defn unflatten [open? close? coll]
(when (seq coll)
(z/root
(reduce
(fn [loc elem]
(cond
(open? elem)
(-> loc (z/append-child (list elem)) z/down z/rightmost)
(and (close? elem) (z/up loc))
(-> loc (z/append-child elem) z/up)
:else (z/append-child loc elem)))
(z/seq-zip ())
coll))))
Это создаетзастегните молнию на пустом списке и создайте его, используя reduce
поверх последовательности ввода.Он принимает пару предикатов для открытия / закрытия групп и допускает произвольно вложенные группы:
(unflatten #{:b} #{:d} [:a :b :c :b :d :d :e :f])
=> (:a (:b :c (:b :d) :d) :e :f)
(unflatten #{:b} #{:d} [:a :b :c :b :d :b :b :d :e :f])
=> (:a (:b :c (:b :d) (:b (:b :d) :e :f)))
(unflatten #{:b} #{:d} [:b :c :e :f])
=> ((:b :c :e :f))
(unflatten #{:b} #{:d} [:d :c :e :f])
=> (:d :c :e :f)
(unflatten #{:b} #{:d} [:c :d])
=> (:c :d)
(unflatten #{:b} #{:d} [:c :d :b])
=> (:c :d (:b))