(cách tiếp cận dựa trên Promise ở phía trên cùng, core.async dựa trên cách tiếp cận thấp xuống. Cả hai ngắn mạch trên giá trị falsey đầu tiên.)
Đây là một phiên bản sử dụng thực tế là một lời hứa duy nhất có thể được gửi nhiều lần (mặc dù chỉ giao hàng đầu tiên sẽ thành công trong việc thiết lập giá trị của nó); giao hàng tiếp theo chỉ đơn giản là trả lại nil
mà không có tác dụng phụ).
(defn thread-and
"Computes logical conjunction of return values of fs, each of which
is called in a future. Short-circuits (cancelling the remaining
futures) on first falsey value."
[& fs]
(let [done (promise)
ret (atom true)
fps (promise)]
(deliver fps (doall (for [f fs]
(let [p (promise)]
[(future
(if-not (swap! ret #(and %1 %2) (f))
(deliver done true))
(locking fps
(deliver p true)
(when (every? realized? (map peek @fps))
(deliver done true))))
p]))))
@done
(doseq [[fut] @fps]
(future-cancel fut))
@ret))
Một số xét nghiệm:
(thread-and (constantly true) (constantly true))
;;= true
(thread-and (constantly true) (constantly false))
;;= false
(every? false?
(repeatedly 100000
#(thread-and (constantly true) (constantly false))))
;;= true
;; prints :foo, but not :bar
(thread-and #(do (Thread/sleep 1000) (println :foo))
#(do (Thread/sleep 3000) (println :bar)))
Đưa ý tưởng của Arthur và A. Webb với nhau, bạn có thể sử dụng core.async tới và kết quả cùng thời gian ngắn mạch trên giá trị falsey đầu tiên trở lại :
(defn thread-and
"Call each of the fs on a separate thread. Return logical
conjunction of the results. Short-circuit (and cancel the calls
to remaining fs) on first falsey value returned."
[& fs]
(let [futs-and-cs
(doall (for [f fs]
(let [c (chan)]
[(future (>!! c (f))) c])))]
(loop [futs-and-cs futs-and-cs]
(if (seq futs-and-cs)
(let [[result c] (alts!! (map peek futs-and-cs))]
(if result
(recur (remove #(identical? (peek %) c)
futs-and-cs))
(do (doseq [fut (map first futs-and-cs)]
(future-cancel fut))
false)))
true))))
Kiểm tra với (constantly false)
và (constantly true)
:
(thread-and (constantly true) (constantly true))
;= true
(thread-and (constantly true) (constantly false))
;= false
;;; etc.
Cũng lưu ý rằng ngắn mạch không thực sự làm việc:
;;; prints :foo before returning false
(thread-and #(do (Thread/sleep 3000) false)
#(do (Thread/sleep 1000) (println :foo)))
;;; does not print :foo
(thread-and #(do (Thread/sleep 3000) false)
#(do (Thread/sleep 7000) (println :foo)))
Nguồn
2013-07-12 21:45:29
Liệu cách tiếp cận mà không hoạt động? Bạn có một vấn đề cụ thể với nó? Nó có vẻ khá tốt với tôi :) –
trong khi nó làm việc, nó đã phải hoàn thành cả hai mà không thiếu nếu một thất bại. Tôi đang tìm cách làm điều tương tự nhanh hơn. – ChadJPetersen
@ChadJPetersen Không, nó không phải hoàn thành cả hai; 'và' mạch ngắn. Nó không _cancel_ nó không sử dụng, nhưng đó là một mối quan tâm riêng biệt (và bạn _could_ hủy bỏ nó, nếu bạn muốn). –