2017-01-30 4 views
0

Я хочу использовать переходные процессы для создания большой структуры данных. Вот код, без переходных процессов:создание структуры временных данных с использованием `repeat`

(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!). Что лучше?

+1

«Я хочу использовать переходные процессы для создания большой структуры данных», пожалуйста, объясните, почему/что вы на самом деле пытаетесь сделать, и каковы ваши ожидания? – birdspider

+0

Мой день 6 для появления кода 2015 медленный. Поэтому стараюсь ускорить это. Сначала подумайте о вызове 'mk-state'. Смотрите: https://github.com/chrismurrph/advent-of-code/blob/master/dev/advent_2015/day06.clj –

+1

'in' уже использует переходные процессы внутри, поэтому, даже если вы сделали это вручную, вы бы не получили никаких ускорив. Сначала вы должны измерить свое узкое место! – ClojureMostly

ответ

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

+0

Из того, что у вас там, легко получить «переходный-повторный», я думаю? Вместо '(conj! Cum cnt)' have '(conj! Cum value)', причем значение является вторым параметром. –

+1

Если более быстрое 'повторение' - это все, что вам нужно, я не уверен, что вы сможете побить встроенную функцию Clojure, которая закодирована в родной Java. –

+2

«Функция' in' использует 'conj' внутри '- фактически' in' автоматически использует переходные процессы, когда это возможно, см. Мой ответ. Кроме того, несмотря на то, что всегда полезно тестировать, цикл «на основе сокращения» на основе векторов чрезвычайно оптимизирован и должен быть значительно быстрее, чем «loop»/'recur'. Таким образом, исходное выражение в тексте вопроса ('(in [] ...)') уже является одним из наилучших возможных подходов ('(vec ...)', являющимся наиболее очевидной одинаково хорошей альтернативой). –

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

У меня появилась идея ускорить работу с https://github.com/bhauman/advent-of-clojure/blob/master/src/advent/day06.clj. Просто мой первый взгляд на переходные процессы, а не на решение какой-либо проблемы с узким местом. Как вы можете видеть по ссылке, чтобы действительно ускорить работу, добавьте больше мутаций в пределах 'стойкого!' Вызова. –

+0

@ChrisMurphy Я понимаю, но называет «transistant» и «perisistant!» Нести (хотя и постоянную) накладные расходы, поэтому, если они хорошо подходят, действительно зависит от основной проблемы - что, как говорится, я ни в коем случае эксперт, и могут быть шаблоны, где их использование в целом благоприятно – birdspider

+0

Я только что предположил, что код Брюса будет намного быстрее из-за использования переходных процессов. Но на самом деле он намного медленнее. Поэтому вы делаете хороший момент. –

6

into использует переходные процессы автоматически всякий раз, когда это возможно.

То есть, первое, что делает into, проверяет, имеет ли его первый аргумент временную версию (реализует ли она clojure.lang.IEditableCollection):

  1. , если это произойдет, то into использует transient + reduce + conj! + persistent (и на вершине, что, with-meta + meta для сохранения метаданных);

  2. в противном случае он использует reduce + conj.

Here's the source as of Clojure 1.8, если вы хотите подтвердить детали. (В частности, обратите внимание, что все вышеизложенное также относится к тройной перегрузке, которая принимает преобразователь.)

Таким образом, ваше исходное выражение, (into [] (repeat 10 :a)), уже использует переходные процессы и является, по сути, лучшим способом использования переходных процессов здесь. Любое явное упоминание о transient/conj!/и т. Д. Совершенно излишне.

 Смежные вопросы

  • Нет связанных вопросов^_^