【问题标题】:Lazy stateful processing with sequence libraries使用序列库进行惰性状态处理
【发布时间】:2014-12-30 10:16:22
【问题描述】:

假设我想处理一个序列,其中处理是有状态的,并且我想使用 Clojure 序列库以一种惰性的方式执行此操作。

作为一个具体的例子,假设我想实现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 #{})))

我对上述内容没有意见,但是,如果可以的话,我更愿意使用mapfilter 之类的方法。我正在努力解决如何干净地做到这一点。一种可行的方法是使用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'' 实现了使用更高级别抽象(mapfilterreductions)的目标,同时保持惰性。但它并没有过于复杂,尤其是我将它作为累加器传递的向量。

如果我尝试将状态直接嵌入到要与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 通常是最干净的方法吗?

【问题讨论】:

  • 似乎是见仁见智。我认为惰性序列方法更清晰。
  • 我的 0.02$:我不认为最后一个是错的,我不理解将 internal 状态变异为 side effects 并且它是一个非常易读的实现,但同意这是一个意见问题
  • 顺便说一句,在从另一个惰性序列生成惰性序列时,您应该避免使用[x & xs] 解构,因为它会强制输入序列中的 两个 元素,以便找出xs 是否应该为零,如果xs 的生产成本很高,这可能会很糟糕。对于库质量代码,您必须使用 firstrest
  • 好点!顺便说一句:我对拥有一个最容易推理正确性的“参考实现”的概念以及优化的“生产”或“库”实现的概念很感兴趣,也许与通过@的参考 impl 进行比较987654342@。我发现distinct 的发货实现很吸引人,它的内部loop/recur 在寻找新元素时避免了lazy-seq

标签: clojure


【解决方案1】:

我喜欢distinct',尽管我会内联process 辅助函数,例如((fn process ...) coll #{})。使用惰性序列和递归来解决问题并没有错,而试图通过将所有内容都变成map/filter 来避免它们会导致程序的可读性大大降低。

如果你不介意拉平地/有用,你可以用lazy-loop使它更漂亮一点:

(defn distinct'''' [coll]
  (lazy-loop [coll coll, seen #{}]
    (when-let [[x & r] (seq coll)]
      (if (contains? seen x)
        (recur r seen)
        (cons x (lazy-recur r (conj seen x)))))))

哪个宏扩展为等同于您的distinct'

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多