10

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.

このような設計は、たとえば、単一の整数値を参照型にラップし、そのコンシューマーからインクリメントするよりも便利だと思います。

  • 提案されたアプローチは、実装の詳細を単一の変更点、つまりジェネレーターに減らします。atomそうしないと、すべての消費者が参照型 ( ) と次の値を提供する具体的な関数( inc)の両方を気にする必要があります。
  • シーケンスは、多くの clojure.core 関数を利用できます。アトムから id のリストを「手動で」構築するのは少しかさばります:(take 4 (repeatedly #(swap! _ inc)))

実用的な実装を思いつくことができませんでした。それはまったく可能ですか?

4

7 に答える 7

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))))

これは機能しますが、シーケンスの先頭を保存しない場合に限ります。頭をキャプチャする変数を作成する場合:

(def id-seq (id-gen))

次に、それを繰り返し呼び出すと、シーケンスの先頭を保持しているため、シーケンスの最初からIDが返されます。

(take 3 id-seq)
;; => (0 1 2)
(take 3 id-seq)
;; => (0 1 2)
(take 3 id-seq)
;; => (0 1 2)

ただし、シーケンスを再作成すると、不純物のために新しい値が得られます。

(take 3 (id-gen))
;; (3 4 5)
(take 3 (id-gen))
;; (6 7 8)
(take 3 (id-gen))
;; (9 10 11)

教育目的(製品コードではない)で次のことを行うことをお勧めしますが、不純物をより直接的に実装する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)
于 2013-01-04T03:04:00.213 に答える
2

あなたの質問に答えている間、私は何かを発見する楽しい時間を過ごしました. 私が最初に思いついたのは、これらの ID が必要な最終的な目的が何であれ、このgensym関数が役立つかもしれないということでした。

次に、「これは、新しい ID を生成するために不純なカウンターをインクリメントしているようだ」「そのソース コードには何が含まれているのか」と考えました。これが私をこれに導きました:

(. clojure.lang.RT (nextID))

あなたが必要とすることをするようです。涼しい!あなたが提案する方法でそれを使用したい場合は、おそらく関数にするでしょう:

(defn generate-id []
  (. clojure.lang.RT (nextID)))

次に、次のことができます。

user> (repeatedly 5 generate-id)
=> (372 373 374 375 376)

これが常に「グローバルに」一意の値を生成するかどうかはまだテストしていません-用語についてはわかりませんがgenerate-id、異なるスレッド内からこの関数を使用している可能性がある場合について話しているが、それでも確認したい独自の価値を生み出しているということです。

于 2013-01-04T02:51:49.030 に答える
1

これはおそらく別の解決策です:

user=> (defn positive-numbers
          ([] (positive-numbers 1))
          ([n] (cons n (lazy-seq (positive-numbers (inc n))))))
#'user/positive-numbers
user=> (take 4 (positive-numbers))
(1 2 3 4)
user=> (take 4 (positive-numbers 5))
(5 6 7 8)
于 2013-01-04T02:35:28.277 に答える
0

シード識別子の任意のコレクションから識別子を作成する:

(defonce ^:private counter (volatile! 0))

(defn- next-int []
  (vswap! counter inc))

(defn- char-range
  [a b]
  (mapv char
        (range (int a) (int b))))

(defn- unique-id-gen
  "Generates a sequence of unique identifiers seeded with ids sequence"
  [ids]
  ;; Laziness ftw:
  (apply concat
         (iterate (fn [xs]
                    (for [x xs
                          y ids]
                      (str x y)))
                  (map str ids))))

(def inf-ids-seq (unique-id-gen (concat (char-range \a \z)
                                        (char-range \A \Z)
                                        (char-range \0 \9)
                                        [\_ \-])))

(defn- new-class
  "Returns an unused new classname"
  []
  (nth inf-ids-seq (next-int)))

(repeatedly 10 new-class)

デモンストレーション:

(take 16 (unique-id-gen [\a 8 \c]))
;; => ("a" "8" "c" "aa" "a8" "ac" "8a" "88" "8c" "ca" "c8" "cc" "aaa" "aa8" "aac" "a8a")
于 2016-12-13T16:41:34.373 に答える
0

これはハックですが、機能し、非常に簡単です

; there be dragons !
(defn id-gen [n] (repeatedly n (fn [] (hash #()))))
(id-gen 3) ; (2133991908 877609209 1060288067 442239263 274390974)

基本的に、clojure は「匿名」関数を作成しますが、clojure 自体がその名前を必要とするため、衝突を避けるために一意の不純な ID を使用します。一意の名前をハッシュする場合は、一意の番号を取得する必要があります。

それが役に立てば幸い

于 2016-02-09T10:56:12.410 に答える
0

別の簡単な方法を次に示します。

user> (defn make-generator [& [ii  init]]
  (let [a (atom (or ii 0 ))
        f #(swap! a inc)]
    #(repeatedly f)))
#'user/make-generator
user> (def g (make-generator))
#'user/g
user> (take 3 (g))
(1 2 3)
user> (take 3 (g))
(4 5 6)
user> (take 3 (g))
(7 8 9)
于 2014-07-12T04:40:37.877 に答える