如何编写一个谓词来检查无限序列中是否存在值?

时间:2010-07-18 20:16:18

标签: clojure predicate lazy-sequences

我今天想到了一个更高阶的功能,我不知道该怎么写。我有几个稀疏,懒惰的无限序列,我想创建一个抽象,让我检查一下给定的数字是否在任何这些惰性序列中。为了提高性能,我想将稀疏序列的值推送到散列映射(或集合),在必要时动态增加散列映射中的值的数量。由于懒惰的seqs的稀疏性,自动记忆不是这里的答案。

可能代码最容易理解,所以这就是我到目前为止所拥有的。如何更改以下代码以使谓词使用封闭式散列映射,但如果需要,会增加散列映射的大小并重新定义自身以使用新的散列映射?

(defn make-lazy-predicate [coll]
  "Returns a predicate that returns true or false if a number is in
  coll. Coll must be an ordered, increasing lazy seq of numbers."
  (let [in-lazy-list? (fn [n coll top cache]
                        (if (> top n)
                          (not (nil? (cache n)))
                          (recur n (next coll) (first coll) 
                                 (conj cache (first coll)))]
    (fn [n] (in-lazy-list? n coll (first coll) (sorted-set)))))

(def my-lazy-list (iterate #(+ % 100) 1))

(let [in-my-list? (make-lazy-predicate my-lazy-list)]
  (doall (filter in-my-list? (range 10000))))

如何在不恢复强制风格的情况下解决此问题?

2 个答案:

答案 0 :(得分:2)

这是Adam解决方案的线程安全变体。

(defn make-lazy-predicate
  [coll]
  (let [state        (atom {:mem #{} :unknown coll})
        update-state (fn [{:keys [mem unknown] :as state} item]
                       (let [[just-checked remainder]
                             (split-with #(<= % item) unknown)]
                         (if (seq just-checked)
                           (-> state
                             (assoc :mem (apply conj mem just-checked))
                             (assoc :unknown remainder))
                           state)))]
    (fn [item]
      (get-in (if (< item (first (:unknown @state)))
                @state
                (swap! state update-state item))
              [:mem item]))))

还可以考虑使用refs,但是谓词搜索可能会被封闭的事务回滚。这可能是也可能不是你想要的。

答案 1 :(得分:1)

此函数基于核心memoize函数的工作原理。只有已从惰性列表中消耗的数字才会缓存在一个集合中。它使用内置的take-while而不是手动进行搜索。

(defn make-lazy-predicate [coll]
  (let [mem (atom #{})
        unknown (atom coll)]
    (fn [item]
      (if (< item (first @unknown))
        (@mem item)
        (let [just-checked (take-while #(>= item %) @unknown)]
          (swap! mem #(apply conj % just-checked))
          (swap! unknown #(drop (count just-checked) %))
          (= item (last just-checked)))))))