使用`repeat`创建瞬态数据结构

时间:2017-01-30 17:07:14

标签: clojure

我想使用瞬态来创建大型数据结构。这是没有瞬变的代码:

(into [] (repeat 10 :a))
;; => [:a :a :a :a :a :a :a :a :a :a]

我天真的尝试就是这样:

(persistent! (into (transient []) (repeat 10 :a)))

我可以通过以下方式收到错误消息:

(into (transient []) (repeat 10 :a))
;; => ClassCastException clojure.lang.PersistentVector$TransientVector cannot be cast to clojure.lang.IPersistentCollection  clojure.core/conj--6410 (core.clj:82)

尝试使用repeat是不是错了? (没有这样的函数repeat!)。什么是更好的方法?

3 个答案:

答案 0 :(得分:6)

function STRtoArray(str) { var lines = str.split('\n'), keys = lines[0].trim().split(','); // getting key fields var result = lines.slice(1).map(function (l){ return l.trim().split(',').reduce(function (r, l, i) { if (i === 1) l = Number(l); // casting 'Age' field to number type if (i === 3) l = Boolean(l === 'true'? 1:0); r[keys[i]] = l; return r; }, {}); }); return result; } var str = 'Name,Age,Car,wife \n John,25,,true\n Ben,31,wolksvagen,false' console.log(STRtoArray(str));会尽可能自动使用瞬变。

也就是说,into做的第一件事就是检查它的第一个参数是否有一个瞬态版本(它是否实现了`clojure.lang.IEditableCollection):

  1. 如果确实如此,则into使用into + transient + reduce + conj!(除此之外,persistent }} with-meta保存元数据);

  2. 否则使用meta + reduce

  3. Here's the source as of Clojure 1.8如果您想确认详情。 (特别要注意的是,上述所有情况都适用于采用换能器的三元过载。)

    所以你的原始表达式conj已经使用了瞬态,实际上是这里使用瞬态的最佳方法。任何明确提及(into [] (repeat 10 :a)) / transient /等的内容都是多余的。

答案 1 :(得分:1)

你确定这是瓶颈,因为你的原始approch对于这个样本大小来说非常快:

(defn mk-state-prev [value width height]
  (vec (repeat height (vec (repeat width value)))))

(time (doall (mk-state-prev 3 1000 1000)))
"Elapsed time: 0.284245 msecs"

(time (get-in (mk-state-prev 3 1000 1000) [100 101] :not-found))
"Elapsed time: 0.305037 msecs" 
3 ; found `3`

这引出了下一个问题:

你是如何测量“时间”的?

关于你的mk-state - 这是我厌倦的一种方法,但它更慢

(defn mk-state [value width height]
  (let [v (transient (vector))
        row (vec (repeat height value))] ; only create 1 row
    (doseq [n (range 0 width)] 
      (conj! v row)) ; mutate (add rows)
    (persistent! v)))

(set! *print-length* 5)
; otherwise the repl will propably hang with printing 1e6 items

(time (doall (mk-state :t 1000 1000)))
"Elapsed time: 1.366543 msecs"

EDIT2:

  

@birdspider你不能把你的瞬态砸到这样的地方。检查返回值,这是不正确的。 - ClojureMostly 16小时前

这怎么不对?不是咆哮 - 请教育我:)

Clojure 1.8.0
(defn mk-state [value width height]
  (let [v (transient (vector))
        row (vec (repeat height value))] ; only create 1 row
    (doseq [n (range 0 width)] 
      (conj! v row)) ; mutate (add rows)
    (persistent! v)))
#'user/mk-state
user=> (get-in (mk-state :t 1000 1000) [99 99])
:t
user=> (pprint (mk-state :t 10 10))
[[:t :t :t :t :t :t :t :t :t :t]
 [:t :t :t :t :t :t :t :t :t :t]
 [:t :t :t :t :t :t :t :t :t :t]
 [:t :t :t :t :t :t :t :t :t :t]
 [:t :t :t :t :t :t :t :t :t :t]
 [:t :t :t :t :t :t :t :t :t :t]
 [:t :t :t :t :t :t :t :t :t :t]
 [:t :t :t :t :t :t :t :t :t :t]
 [:t :t :t :t :t :t :t :t :t :t]
 [:t :t :t :t :t :t :t :t :t :t]]
nil

答案 2 :(得分:0)

into函数在内部使用conj。对于瞬态,您需要使用conj!函数。例如:

(defn transient-range [limit]
  (loop [cnt 0 
         cum (transient []) ]
    (if (< cnt limit)
      (recur  (inc cnt)  (conj! cum cnt))
      (persistent! cum))))

(println (transient-range 20))

;=> [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]

对于瞬态repeat,请执行以下操作:

(defn transient-repeat [qty value]
  (loop [cnt 0 
         cum (transient []) ]
    (if (< cnt qty)
      (recur  (inc cnt)  (conj! cum value))
      (persistent! cum))))

(println (transient-repeat 5 42))

;=> [42 42 42 42 42]

请在此处查看详细信息:https://clojuredocs.org/clojure.core/transient