Сырой язык Clojure отчета о ходе выполнения функции


Цель этой функции-отчет о обработки на терминал. Он перебирает массив карт, которые содержат два свойства: :последовательности и функции. В каждую последовательность-функция-карте, это будет цикл через последовательность, и выполнить функцию для каждого элемента в последовательности.

Это моя первая программа на Clojure, который будет использоваться в производственной среде. Эта функция делает работу, но я подозреваю, что это не очень lispy.

(defn run-with-reporting [sequence-function-maps stop-time]
  (def beginning-time (java.util.Date.))
  (let [seq-counts (map #(count (:sequence %)) sequence-function-maps)]
    (def total-sequence-counts (reduce + seq-counts)))
  (println (str "total counts = " total-sequence-counts))
  (def prog-rpt-ref (ref {:todo total-sequence-counts, :done 0, :total-time-taken 0}))
  (doseq [sequence-function-map sequence-function-maps]
    (let [sequence (:sequence sequence-function-map),
          function (:function sequence-function-map)]
      (loop [loop-seq sequence]

        (if (and stop-time (clj-time/after? (clj-time/now) stop-time))
          (println "stopped at " (clj-time/now) ". Requested stop at " stop-time)
          (if loop-seq
            (do
              (def item (first loop-seq))
              (def start-time (java.util.Date.))
              (function item)
              (def end-time (java.util.Date.))
              (def time-delta (- (.getTime end-time) (.getTime start-time)))
              (let [derefd-rpt (deref prog-rpt-ref)]
                (dosync (alter prog-rpt-ref assoc
                               :done (inc (:done derefd-rpt)),
                               :total-time-taken (+ (:total-time-taken derefd-rpt) time-delta))))
              (let [derefd-rpt (deref prog-rpt-ref)]
                (let [average-time (/ (:total-time-taken derefd-rpt) (:done derefd-rpt))]
                  (println "Avg time / each = " (hrs-min-sec average-time)
                           ", Estimated total time left = "
                           (hrs-min-sec (* average-time (- (:todo derefd-rpt) (:done derefd-rpt)))))))
              (recur (next loop-seq))))))))
  (let [derefd-rpt (deref prog-rpt-ref)]
    (println "Total time taken = " (hrs-min-sec (- (.getTime (java.util.Date.)) (.getTime beginning-time))) ", Done = " (:done derefd-rpt) "/" (:todo derefd-rpt))))

Вот функция тестирования

(defn test-run-w-reporting [stop-clj-time]
  (def testing-func (fn [item] (. java.lang.Thread sleep (rand 1000))))
  (let [sequence-function-maps [{:sequence (range 30), :function testing-func},
                               {:sequence (range 15), :function testing-func}]]
    (run-with-reporting sequence-function-maps stop-clj-time)))


525
4
задан 23 сентября 2011 в 03:09 Источник Поделиться
Комментарии
1 ответ

Я укажу вещи, которые я бы сделал по-другому, хотя я не эксперт в Clojure, у меня есть некоторый опыт в этом. Пронумерованные элементы будут появляться в коде в виде комментариев.


  1. нет деф внутри тела определение

    • деф будет определять VAR для всего пространства имен таким образом, вы будете pollude пространство имен с Вэнс

    • давайте использовать вместо


  2. не надо внутри тела, если

    • кроме того, я нахожу это более идиоматические использовать, когда в случае, если нет другой отрасли существует


  3. используя @ вместо оператор deref может уменьшить многословие

  4. с помощью обновления-В вместо Inc, вы можете просто передать функции преобразования

    • однако обратите внимание на векторных обозначений: обновление в вектор запросам


  5. Вы можете пройти частичную функцию, т. е. функцию, которая является
    частично применяется в качестве функции преобразования для обновления всего-затраченное время

  6. Не нужно гнездо позволяет, Вы можете написать все определения в единое давайте

Код:

(defn run-with-reporting [sequence-function-maps stop-time]
(let [beginning-time (java.util.Date.) ;; 1.
seq-counts (map #(count (:sequence %)) sequence-function-maps)
total-sequence-counts (reduce + seq-counts)
prog-rpt-ref (ref {:todo total-sequence-counts, :done 0, :total-time-taken 0})]
(println (str "total counts = " total-sequence-counts))
(doseq [sequence-function-map sequence-function-maps]
(let [sequence (:sequence sequence-function-map),
function (:function sequence-function-map)]
(loop [loop-seq sequence]
(if (and stop-time (clj-time/after? (clj-time/now) stop-time))
(println "stopped at " (clj-time/now) ". Requested stop at " stop-time)
(when loop-seq ;; 2.
(let [item (first loop-seq)
start-time (java.util.Date.)]
(function item)
(let [end-time (java.util.Date.)
time-delta (- (.getTime end-time) (.getTime start-time))
derefd-rpt @prog-rpt-ref] ;; 3.
(dosync (alter prog-rpt-ref update-in ;; 4.
[:done] inc,
[:total-time-taken] (partial + time-delta))) ;; 5.
(let [derefd-rpt @prog-rpt-ref ;; 3. & 6.
average-time (/ (:total-time-taken derefd-rpt) (:done derefd-rpt))]
(println "Avg time / each = " (hrs-min-sec average-time)
", Estimated total time left = "
(hrs-min-sec (* average-time (- (:todo derefd-rpt) (:done derefd-rpt)))))))
(recur (next loop-seq))))))))
(let [derefd-rpt (deref prog-rpt-ref)]
(println "Total time taken = " (hrs-min-sec (- (.getTime (java.util.Date.)) (.getTime beginning-time))) ", Done = " (:done derefd-rpt) "/" (:todo derefd-rpt))))
)


  1. Я попытаюсь сократить многословие и увеличить читабельность (в
    грамотный смысле), используя описательные вспомогательные функции -
    текущее время вместо (.методов gettime (Ява.утиль.Дата.))

  2. Лично я хотел бы сохранить количество переменных, как низко как
    возможно. Переменные великолепно подходят для создания побочных эффектов,
    описывая сложные результаты или, когда вам нужен результат несколько
    раз. В случае Реф, пишу @ вместо использования
    временный мне кажется более естественным.

    В качестве таких:


    • Я хотел бы попробовать, чтобы уменьшить количество побочных эффектов и

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


  3. Вы можете использовать destructring для карт

    • (пусть [{:кнопки [ключ1 ключ2]} mymap] ...) позволит привязать key1 и KEY2 путем извлечения :ключ1 & :ключ2 от mymap


Код:

(defn current-time
"Return the current time. Same as (.getTime (java.util.Date.))."
[]
(.getTime (java.util.Date.)))

(defn run-with-reporting [sequence-function-maps stop-time]
(let [beginning-time (current-time) ;; 1.
seq-counts (map #(count (:sequence %)) sequence-function-maps)
total-sequence-counts (reduce + seq-counts)
prog-rpt-ref (ref {:todo total-sequence-counts, :done 0, :total-time-taken 0})]
(println (str "total counts = " total-sequence-counts))
(doseq [sequence-function-map sequence-function-maps]
(let [{:keys [sequence function]} sequence-function-map] ;; 9.
(loop [loop-seq sequence]
(if (and stop-time (clj-time/after? (clj-time/now) stop-time))
(println "stopped at " (clj-time/now) ". Requested stop at " stop-time)
(when loop-seq ;; 2.
(let [item (first loop-seq)
start-time (current-time)] ;; 7.
(function item)
(dosync (alter prog-rpt-ref update-in ;; 4.
[:done] inc,
[:total-time-taken] (partial + (- (current-time) start-time)))) ;; 5., 7. & 8.
(let [average-time (/ (:total-time-taken @prog-rpt-ref) (:done @prog-rpt-ref))] ;; 8.
(println "Avg time / each = " (hrs-min-sec average-time)
", Estimated total time left = "
(hrs-min-sec (* average-time (- (:todo @prog-rpt-ref) (:done @prog-rpt-ref)))))) ;; 8.
(recur (next loop-seq))))))))
(println "Total time taken = " (hrs-min-sec (- (current-time) beginning-time))
", Done = " (:done @prog-rpt-ref)
"/" (:todo @prog-rpt-ref))))

Теперь, давайте взглянем на саму функцию. В общем, когда
писать на Clojure, вы должны попробовать, чтобы сохранить ваши функции универсального
и просто. Сохраняя их простой и сосредоточившись только на одном
задач, они обычно легче для создания и повторного использования.
Таким образом, я хотел бы рекомендовать разделение функции.

В вашем случае, что вы хотите сделать, это действительно:


  1. карта функция для последовательности: в Clojure уже предоставляет функции для этого, а именно карту.

  2. остановить операцию сопоставления, если указанное время уже достигнуто.

  3. распечатать среднем раз в звонок

  4. распечатать общее время

Первый пункт тривиален.

Для второй точки, мы можем использовать тот факт, что в Clojure есть ленивые
последовательности. Это позволяет нам использовать встроенный в то время как функция
с предикат, который проверяет время и останавливается, когда он прошел
стоп-время:

(defn take-until
"Returns a lazy seq of items from coll util the STOP-TIME has been reached."
[stop-time coll]
(take-while
(fn [item]
(if (not (> (current-time) stop-time))
true
(do (println "stopped at " (current-time) ". Requested stop at " stop-time)
false)))
coll))

Третий пункт может быть достигнуто путем простого сопоставления по коллекции
и распечатывать необходимую информацию. Это правда
просто то, что вы сделали, но преобразована в функцию, которая работает на любом
последовательность:

(defn measure-coll-retrieval
"Returns a lazy seq of items from coll. Will print to stdout the
average time between element extractions."
;; you can use destructuring inside the argument list as well, here
;; we're additionaly specifying some defaults for when the caller
;; does not provide a value
[coll & {:keys [start-count total-count] :or {start-count 0 total-count nil}}]
(let [beginning-time (current-time)]
(map-indexed
(fn [index item]
(let [index (+ start-count index 1)
average-time (/ (- (current-time) beginning-time) index)]
(print "Avg time / each = " (hrs-min-sec average-time))
(if total-count
(println ", Estimated total time left = "
(hrs-min-sec (* average-time (- total-count index))))
(println))
item))
coll)))

Прежде чем писать функцию отчетности, маленький помощник, поэтому мы можем
обойти сл-лязг в Clojure:

(defn unchunk
"takes a chunked sequence and turns it into an unchunked sequence"
[s]
(lazy-seq
(when-let [[x] (seq s)]
(cons x (unchunk (rest s))))))

Теперь мы можем написать функцию отчетности:

(defn run-with-reporting [sequence-function-maps stop-time]
(let [beginning-time (current-time) ;; 1.
seq-counts (map (comp count :sequence) sequence-function-maps)
total-sequence-counts (reduce + seq-counts)
intermed-count (atom 0)]
(println (str "total counts = " total-sequence-counts))
;; note how it is possible to immediately destructure the map
(doseq [{:keys [sequence function]} sequence-function-maps]
(swap! intermed-count
+
(count
;; this is where everything happens: apply function,
;; take-until stop-time reached & measure average time
(measure-coll-retrieval
(take-until stop-time (map function (unchunk sequence)))
:start-count @intermed-count
:total-count total-sequence-counts))))
(println "Total time taken = " (hrs-min-sec (- (current-time) beginning-time))
", Done = " @intermed-count
"/" total-sequence-counts)))

(defn test-run-w-reporting [stop-clj-time]
(def testing-func (fn [item] (. java.lang.Thread sleep (rand 1000))))
(let [sequence-function-maps [{:sequence (range 30), :function testing-func},
{:sequence (range 15), :function testing-func}]]
(run-with-reporting sequence-function-maps stop-clj-time)))

Я надеюсь, что это помогло вам немного.

С уважением,
Томас

5
ответ дан 18 января 2012 в 07:01 Источник Поделиться