Предположим, что я хочу обработать последовательность, где обработка выполнена с сохранением состояния, и я хочу сделать это ленивым способом с использованием библиотек последовательности Clojure.Lazy обработка состояния с библиотеками последовательностей
В качестве конкретного примера предположим, что я хочу реализовать distinct
, который, естественно, реализован как фильтр состояния, отслеживая видимые элементы. Мой первый удар является не вступать в использовании библиотек последовательностей, но вместо этого lazy-seq
:
(defn distinct' [coll]
(let [process (fn process [coll seen]
(lazy-seq
(when-let [[x & r] (seq coll)]
(if (contains? seen x)
(process r seen)
(cons x (process r (conj seen x)))))))]
(process coll #{})))
Я в порядке с вышеизложенным, но я бы предпочел, чтобы вместо этого использовать методы как map
и filter
если я могу , Я изо всех сил пытаюсь это сделать. Один из подходов, который работает, чтобы использовать reductions
:
(defn distinct'' [coll]
(->> (reductions (fn [[_ _ seen] x]
(if (contains? seen x)
[false nil seen]
[true x (conj seen x)]))
[false nil #{}]
coll)
(filter first)
(map second)))
Фундаментально, distinct''
выполняет цели использования абстракций более высокого уровня (map
, filter
, reductions
), оставаясь ленивым. Но он не может быть слишком сложным, особенно с вектором, который у меня есть, проходящим в качестве аккумулятора.
Если я пытаюсь напрямую внедрить состояние в предикат, который будет использоваться с filter
, в то время как он ближе к тому, что я представляю, это просто кажется «неправильным», и я смущен даже написать следующее код (filter
«ы документы даже сказать, что предикат должен быть свободен от побочных эффектов):
(defn distinct''' [coll]
(let [seen (atom #{})]
(filter (fn [x]
(if (contains? @seen x)
false
(do (swap! seen conj x)
true)))
coll)))
Мой вопрос:
есть ли способ сделать ленивую динамическую обработку, как это с библиотеками последовательностей в чистом (даже для этого примера distinct
)? Или, lazy-seq
часто самый чистый подход?
Похоже, это вопрос мнения. Я думаю, что ленивый подход более ясен. – Bill
Мои 0.02 $: Я не думаю, что последний ошибочен, я не понимаю, что мутирует * внутреннее * состояние как * побочные эффекты *, и это очень читаемая реализация, но согласилась, что это вопрос мнения вообще –
As в стороне, вам следует избегать использования '[x & xs]' destructuring при создании ленивого seq из другого ленивого seq, потому что он заставляет * два * элемента из входного seq, чтобы выяснить, должно ли xs' быть nil или нет, что может быть плохо, если 'xs' дорого производить. Для кода качества библиотеки вы должны использовать 'first' и' rest'. – amalloy