2013-01-04 35 views
10

Tôi muốn biết cách tạo một chuỗi các giá trị duy nhất không xác định, vô hạn trong Clojure.Xây dựng trình tạo id lười biếng, không tinh khiết

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

Tôi nghĩ rằng thiết kế như vậy có thể thuận tiện hơn so với ví dụ: bao gồm một giá trị số nguyên duy nhất vào một loại tham chiếu và tăng giá trị đó từ người tiêu dùng của nó, như:

  • Cách tiếp cận được đề xuất giảm chi tiết thực hiện cho một điểm thay đổi: máy phát. Nếu không, tất cả người tiêu dùng sẽ phải quan tâm đến cả loại tham chiếu (atom) và hàm bê tông cung cấp giá trị tiếp theo (inc)
  • Trình tự có thể tận dụng nhiều chức năng clojure.core. 'Thủ công' xây dựng một danh sách các id ra khỏi một nguyên tử sẽ có một chút cồng kềnh: (take 4 (repeatedly #(swap! _ inc)))

Tôi không thể thực hiện công việc. Có thể không?

+0

'(lặp đi lặp lại gensym)'. Không phải những gì bạn đang tìm kiếm, tôi biết. Hoặc: '(liên tụC# (gensym" ")))', tạo ra các ký hiệu trông giống như số. – Mars

+0

Hoặc cho số thực tế: '(nhiều lần # (Dài. (Str (gensym" "))))))' – Mars

Trả lời

6

Bạn có thể quấn một chuỗi lười biếng xung quanh một lớp bất tịnh (như một java.util.concurrent.atomic.AtomicLong) để tạo ra một chuỗi id:

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

(defn id-gen [] 
    (cons 
    (.getAndIncrement id-counter) 
    (lazy-seq 
    (id-gen)))) 

này hoạt động, nhưng chỉ khi bạn không lưu phần đầu của chuỗi.Nếu bạn tạo một var để chụp đầu:

(def id-seq (id-gen)) 

Sau đó gọi nó lặp đi lặp lại, nó sẽ trở lại id từ đầu dãy, bởi vì bạn đã được tổ chức vào đầu của chuỗi:

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

Nếu bạn tái tạo chuỗi tuy nhiên, bạn sẽ nhận được giá trị tươi vì sự ô uế:

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

tôi chỉ khuyên bạn nên làm như sau cho các mục đích giáo dục (không phải mã sản xuất), nhưng bạn có thể tạo dụ của riêng bạn ISeq mà thực hiện các tạp chất trực tiếp hơn:

(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) 
+0

Awesome :) Trong khi tôi thường coi trọng bất biến, tôi tự hỏi, trong đó những cách nào mà độ chính xác của ứng dụng có thể bị ảnh hưởng nếu chúng tôi chọn ISeq để sửa đổi. – vemv

+0

btw 'next' được triển khai sai – vemv

+0

bạn có thể xây dựng không? Những gì cần phải được thay đổi? (cảm ơn vì đã chỉ ra btw) –

1

đây là một giải pháp khác, có lẽ:

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

Tôi không nghĩ rằng giải pháp của bạn (mà là khá thú vị anyway) phù hợp với yêu cầu của tôi - nó là tinh khiết! – vemv

2

Tôi đã có một thời gian vui vẻ khám phá một cái gì đó trong việc trả lời câu hỏi của bạn. Điều đầu tiên xảy ra với tôi là có lẽ, cho bất kỳ mục tiêu cuối cùng nào bạn cần các ID này, hàm gensym có thể hữu ích.

Sau đó, tôi nghĩ "ồ, điều đó dường như tăng một số truy cập không tinh khiết để tạo ID mới" và "vâng, có gì trong mã nguồn cho điều đó?" Điều này đã dẫn tôi đến điều này:

(. clojure.lang.RT (nextID)) 

Điều đó dường như làm những gì bạn cần. Mát mẻ! Nếu bạn muốn sử dụng nó theo cách bạn đề nghị, sau đó tôi có lẽ sẽ làm cho nó một chức năng:

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

Sau đó, bạn có thể làm:

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

tôi chưa kiểm tra liệu điều này sẽ sản xuất luôn Các giá trị duy nhất "toàn cầu" - Tôi không chắc về thuật ngữ, nhưng tôi đang nói về khi bạn có thể sử dụng hàm generate-id này từ các chủ đề khác nhau, nhưng vẫn muốn chắc chắn rằng nó tạo ra các giá trị duy nhất.

+0

+1 cho lặn vào trong Clojure impl, tôi chỉ thích học cách nó hoạt động. Nhưng sau khi '(def a (liên tục sinh-id))', '(mất 5 a)' sẽ luôn luôn trả lại như cũ ... – vemv

+0

Khi nhìn lại, tôi thấy câu trả lời của tôi chỉ là một cách khác để làm những gì bạn đã làm nói rằng bạn không muốn. –

0

Một cách đó sẽ là nhiều thành ngữ, thread-an toàn, và mời gọi không weirdness qua tài liệu tham khảo đầu sẽ được sử dụng một đóng cửa qua một trong clojures xây dựng trong tài liệu tham khảo có thể thay đổi. Đây là một mẫu nhanh tôi đã làm việc kể từ khi tôi gặp vấn đề tương tự. Nó chỉ đơn giản là đóng trên một ref.

(def id-generator (let [counter (ref 0)] 
       (fn [] (dosync (let [cur-val @counter] 
         (do (alter counter + 1) 
          cur-val)))))) 

Mỗi khi bạn gọi (id-generator), bạn sẽ nhận được số tiếp theo trong chuỗi.

0

Đây là một cách nhanh chóng:

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

Đây là Hack nhưng nó hoạt động và nó là vô cùng đơn giản

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

Về cơ bản clojure tạo ra một chức năng 'vô danh' nhưng vì clojure itselfs cần một tên cho rằng, nó sử dụng các id không tinh khiết duy nhất để tránh bị thu thập. Nếu bạn băm một tên duy nhất thì bạn sẽ nhận được một số duy nhất.

Hy vọng nó giúp

0

Tạo định từ một bộ sưu tập độc đoán của định danh hạt giống:

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

diễn:

(take 16 (unique-id-gen [\a 8 \c])) 
;; => ("a" "8" "c" "aa" "a8" "ac" "8a" "88" "8c" "ca" "c8" "cc" "aaa" "aa8" "aac" "a8a") 
Các vấn đề liên quan