构建一个懒惰的,不纯的id生成器

vem*_*emv 10 clojure

我想知道如何在Clojure中创建一个无限的,不纯的唯一值序列.

(def generator ...) ; def, not defn
(take 4 generator) ; => (1 2 3 4)
(take 4 generator) ; => (5 6 7 8). note the generator's impurity.
Run Code Online (Sandbox Code Playgroud)

我认为这样的设计可能比例如将一个整数值包装到引用类型并从其使用者中增加它更方便,如下所示:

  • 所提出的方法将实现细节简化为单个变化点:生成器.否则所有的消费者都必须关心引用类型(atom)和提供下一个值的具体函数(inc)
  • 序列可以利用许多clojure.core函数."手动"从原子构建一个id列表会有点笨重:(take 4 (repeatedly #(swap! _ inc)))

我无法想出一个有效的实施方案.有可能吗?

Kyl*_*ton 6

您可以围绕一个不纯的类(如java.util.concurrent.atomic.AtomicLong)包装一个惰性序列来创建一个id序列:

(def id-counter (java.util.concurrent.atomic.AtomicLong.))

(defn id-gen []
  (cons
   (.getAndIncrement id-counter)
   (lazy-seq
     (id-gen))))
Run Code Online (Sandbox Code Playgroud)

这样可行,但前提是您不保存序列的头部.如果你创建一个捕获头部的var:

(def id-seq (id-gen))
Run Code Online (Sandbox Code Playgroud)

然后重复调用它,它将从序列的开头返回id,因为你已经抓住了序列的头部:

(take 3 id-seq)
;; => (0 1 2)
(take 3 id-seq)
;; => (0 1 2)
(take 3 id-seq)
;; => (0 1 2)
Run Code Online (Sandbox Code Playgroud)

如果您重新创建序列,由于杂质,您将获得新的值:

(take 3 (id-gen))
;; (3 4 5)
(take 3 (id-gen))
;; (6 7 8)
(take 3 (id-gen))
;; (9 10 11)
Run Code Online (Sandbox Code Playgroud)

我建议您仅出于教育目的(而非生产代码)执行以下操作,但您可以创建自己的ISeq实例,以更直接地实现杂质:

(def custom-seq
     (reify clojure.lang.ISeq
            (first [this] (.getAndIncrement id-counter))
            (next  [this] (.getAndIncrement id-counter))
            (cons  [this thing]
                   (cons thing this))
            (more [this] (cons
                          (.getAndIncrement id-counter)
                          this))
            (count [this] (throw (RuntimeException. "count: not supported")))
            (empty [this] (throw (RuntimeException. "empty: not supported")))
            (equiv [this obj] (throw (RuntimeException. "equiv: not supported")))
            (seq   [this] this)))

(take 3 custom-seq)
;; (12 13 14)
(take 3 custom-seq)
;; (15 16 17)
Run Code Online (Sandbox Code Playgroud)