33

Tôi nên theo dõi tiến độ của hàm được ánh xạ trong clojure như thế nào?Clojure thành ngữ để báo cáo tiến độ?

Khi xử lý hồ sơ theo ngôn ngữ bắt buộc, tôi thường xuyên in một tin nhắn thường xuyên để cho biết mọi thứ đã biến mất bao lâu, ví dụ: báo cáo mỗi 1000 hồ sơ. Về cơ bản điều này là đếm lặp lại vòng lặp.

Tôi đã tự hỏi những cách tiếp cận nào tôi có thể thực hiện điều này trong clojure, nơi tôi đang ánh xạ một hàm trên chuỗi bản ghi của mình. Trong trường hợp này, in thông báo (và thậm chí giữ số lượng tiến bộ) dường như là những tác dụng phụ cơ bản.

Những gì tôi đã đưa ra cho đến nay trông giống như:

(defn report 
    [report-every val cnt] 
    (if (= 0 (mod cnt report-every)) 
    (println "Done" cnt)) 
    val) 

(defn report-progress 
    [report-every aseq] 
    (map (fn [val cnt] 
      (report report-every val cnt)) 
     aseq 
     (iterate inc 1))) 

Ví dụ:

user> (doall (report-progress 2 (range 10))) 
Done 2 
Done 4 
Done 6 
Done 8 
Done 10 
(0 1 2 3 4 5 6 7 8 9) 

Có khác (tốt hơn) cách để đạt được hiệu ứng này?

Có bất kỳ cạm bẫy nào trong những gì tôi đang làm không? (Tôi nghĩ rằng tôi đang giữ gìn sự lười biếng và không giữ đầu ví dụ.)

Trả lời

32

Điều tuyệt vời về clojure là bạn có thể đính kèm báo cáo vào chính dữ liệu thay vì mã thực hiện tính toán. Điều này cho phép bạn tách các phần riêng biệt một cách hợp lý này. Dưới đây là một đoạn từ misc.clj của tôi mà tôi tìm thấy tôi sử dụng trong chỉ là về tất cả các dự án:

(defn seq-counter 
    "calls callback after every n'th entry in sequence is evaluated. 
    Optionally takes another callback to call once the seq is fully evaluated." 
    ([sequence n callback] 
    (map #(do (if (= (rem %1 n) 0) (callback)) %2) (iterate inc 1) sequence)) 
    ([sequence n callback finished-callback] 
    (drop-last (lazy-cat (seq-counter sequence n callback) 
        (lazy-seq (cons (finished-callback)())))))) 

sau đó quấn xung quanh phóng viên dữ liệu của bạn và sau đó vượt qua kết quả cho chức năng xử lý.

(map process-data (seq-counter inc-progress input)) 
+1

Tôi nghĩ rằng tôi đang làm một cái gì đó thô lỗ tương tự ở trên, đính kèm báo cáo vào một seq mà bất cứ điều gì có thể được thực hiện. Tôi đã hình dung gắn nó vào một chuỗi các kết quả nhưng nó cũng có thể là chuỗi đầu vào. Mã của bạn đẹp hơn nhiều. Tôi đã không tiến bộ (tha thứ cho việc chơi chữ) để sử dụng một cuộc gọi lại cho thông điệp báo cáo (hoặc chức năng tổng quát hơn) và tôi đã gọi hàm báo cáo cho mọi giá trị. –

+1

Có bất kỳ nơi nào bạn chia sẻ cho misc.clj không? Tôi chắc chắn sẽ được hưởng lợi từ việc xem các ý tưởng khác và triển khai các nội dung hữu ích như truy cập –

+1

có nghĩa là nó thực sự giống như ví dụ ban đầu của bạn, tôi đã có một chút nhanh trên "ohh thats in misk.clj" với hiểu đúng cách câu hỏi . http://code.google.com/p/cryptovide/source/browse/src/com/cryptovide/misc.clj. –

4

Tôi không biết cách nào để thực hiện điều đó, có thể bạn nên duyệt qua tài liệu clojure.contrib để xem có gì chưa. Trong khi chờ đợi, tôi đã xem xét ví dụ của bạn và xóa nó đi một chút.

(defn report [cnt] 
    (when (even? cnt) 
    (println "Done" cnt))) 

(defn report-progress [] 
    (let [aseq (range 10)] 
    (doall (map report (take (count aseq) (iterate inc 1)))) 
    aseq)) 

Bạn đang đi đúng hướng, mặc dù ví dụ này quá đơn giản. Điều này đã cho tôi ý tưởng về một phiên bản tổng quát hơn về chức năng tiến hành báo cáo của bạn. Hàm này sẽ lấy một hàm giống như bản đồ, hàm sẽ được ánh xạ, một hàm báo cáo và một tập các bộ sưu tập (hoặc một giá trị hạt giống và một bộ sưu tập để thử nghiệm giảm).

(defn report-progress [m f r & colls] 
    (let [result (apply m 
       (fn [& args] 
        (let [v (apply f args)] 
        (apply r v args) v)) 
       colls)] 
    (if (seq? result) 
     (doall result) 
     result))) 

Seq? một phần là chỉ có để sử dụng với giảm mà không nhất thiết phải trả về một chuỗi. Với chức năng này, chúng ta có thể viết lại ví dụ của bạn như thế này:

user> 
(report-progress 
    map 
    (fn [_ v] v) 
    (fn [result cnt _] 
    (when (even? cnt) 
     (println "Done" cnt))) 
    (iterate inc 1) 
    (range 10)) 

Done 2 
Done 4 
Done 6 
Done 8 
Done 10 
(0 1 2 3 4 5 6 7 8 9) 

thử nghiệm chức năng lọc:

user> 
(report-progress 
    filter 
    odd? 
    (fn [result cnt] 
    (when (even? cnt) 
     (println "Done" cnt))) 
    (range 10)) 

Done 0 
Done 2 
Done 4 
Done 6 
Done 8 
(1 3 5 7 9) 

Và ngay cả những chức năng giảm:

user> 
(report-progress 
    reduce 
    + 
    (fn [result s v] 
    (when (even? s) 
     (println "Done" s))) 
    2 
    (repeat 10 1)) 

Done 2 
Done 4 
Done 6 
Done 8 
Done 10 
12 
+1

Tôi không nghĩ rằng bạn hiểu những gì tôi đã cố gắng để làm với 'doall' (xin lỗi vì mã tệ hại và không rõ ràng). Tôi đã chỉ sử dụng doall để kiểm tra báo cáo tại repl để buộc báo cáo xử lý toàn bộ chuỗi (nếu không nó sẽ được đánh giá lười biếng). doall không phải là một phần của chức năng báo cáo đã cố gắng của tôi hoặc xử lý chuỗi dự kiến. –

6

tôi có lẽ sẽ thực hiện báo cáo trong một đại lý. Một cái gì đó như thế này:

(defn report [a] 
    (println "Done " s) 
    (+ 1 s)) 

(let [reports (agent 0)] 
    (map #(do (send reports report) 
      (process-data %)) 
     data-to-process) 
+1

Đó là một cách tiếp cận thú vị. Nghiêm túc báo cáo không hiển thị trong repl của tôi nếu tôi sử dụng chế độ slime trong emacs nhưng in trong một repl bình thường. –

+1

Về sự phản chiếu hơn nữa, tôi có thể tăng thêm mọi thứ trong hàm được gửi tới tác nhân. Việc in ấn của tiến trình có thể là một chức năng thường xuyên tại repl mà truy cập trạng thái của tác nhân. –

+1

Điểm tốt thực sự. Trong thực tế, nếu bạn đang cập nhật một GUI, bạn có thể phải làm điều đó trong chủ đề chính (hoặc trì hoãn nó vào chủ đề chính, dispatchLater vv) anyway. – Dan

0

Tôi gặp sự cố này với một số ứng dụng chạy chậm (ví dụ: cơ sở dữ liệu ETL, v.v.).Tôi đã giải quyết nó bằng cách thêm hàm (tupelo.misc/dot ...)to the tupelo library. Mẫu:

(ns xxx.core 
    (:require [tupelo.misc :as tm])) 

(tm/dots-config! {:decimation 10}) 
(tm/with-dots 
    (doseq [ii (range 2345)] 
    (tm/dot) 
    (Thread/sleep 5))) 

Output:

 0 .................................................................................................... 
    1000 .................................................................................................... 
    2000 ................................... 
    2345 total 

tài liệu API cho các namespace tupelo.misc can be found here.

Các vấn đề liên quan