2012-09-13 13 views
6

Tôi có hai hàm chi phí độc lập. Tôi muốn chạy chúng song song. Tôi không muốn đối phó với tương lai và như vậy (tôi mới đến Clojure và dễ dàng nhầm lẫn).Chết đơn giản Ngã ba tham gia đồng thời trong Clojure

Tôi đang tìm cách đơn giản để chạy đồng thời hai chức năng. Tôi muốn nó hoạt động như sau

(defn fn1 [input] ...) ; costly 
(defn fn2 [input] ...) ; costly 

(let [[out1 out2] (conc (fn1 x) (fn2 y))] ...) 

Tôi muốn điều này trả về véc tơ với một cặp đầu ra. Nó chỉ nên trở lại khi cả hai luồng đã kết thúc. Lý tưởng nhất là conc nên làm việc cho bất kỳ số lượng đầu vào nào. Tôi nghi ngờ đây là một mô hình đơn giản.

+0

Khi bạn nói rằng bạn không muốn đối phó với tương lai, điều đó có nghĩa là bạn không muốn tương lai được sử dụng trong hàm "conc"? Nó là thành ngữ để sử dụng một trong những nguyên tố đồng thời Clojure trong trường hợp, theo như tôi biết, mặc dù chúng có thể được ẩn khỏi bạn thông qua đóng gói trong "conc." – JohnJ

+0

Chắc chắn một số nguyên thủy đồng thời sẽ được sử dụng. conc có thể phức tạp như bạn muốn. Tôi chỉ không muốn đối phó với họ như một người dùng. Tôi nghi ngờ điều này là "bắt đầu một tương lai cho mỗi đầu vào", "chờ đợi trên mỗi đầu ra", "trở lại". Có lẽ nó sẽ phải là một vĩ mô, không chắc chắn. – MRocklin

+1

Chắc chắn là một macro nếu bạn muốn trì hoãn việc đánh giá các đối số để đồng nhất với các luồng. Tôi đang làm việc trên vĩ mô def'n bây giờ. – JohnJ

Trả lời

3

Bạn cần một macro để duy trì cú pháp mong muốn, mặc dù có nhiều cách khác để có được cùng một hành vi, như các câu trả lời khác cho biết. Dưới đây là một cách để làm điều đó:

(defn f1 [x] (Thread/sleep 500) 5) 
(defn f2 [y] 2) 

(defmacro conc [& exprs] 
    `(map deref 
     [[email protected](for [x# exprs] `(future ~x#))])) 

(time (let [[a b] (conc (f1 6) (f2 7))] 
     [a b])) 
; "Elapsed time: 500.951 msecs" 
;= (5 2) 

Việc mở rộng cho thấy cách hoạt động:

(macroexpand-1 '(conc (f1 6) (f2 7))) 
;= (clojure.core/map clojure.core/deref [(clojure.core/future (f1 6)) 
;=          (clojure.core/future (f2 7))]) 

Bạn đã ghi rõ hai chức năng nhưng điều này sẽ làm việc với bất kỳ số lượng các biểu thức.

+0

Tuyệt vời. Điều này đã truyền cảm hứng cho tôi tự mình thử nó. Tôi đã cố gắng để làm cho nó bằng cách thay thế cho với '(bản đồ tương lai exprs)'. Đáng buồn là điều này không có tác dụng vì tương lai chính nó là một vĩ mô. Bất kỳ suy nghĩ về cách này có thể được giảm hơn nữa? – MRocklin

+0

Công việc tuyệt vời bao gồm mã được tạo. – MRocklin

+1

Vâng, đó là sự cố với các macro - không thể tạo chúng dễ dàng như các hàm. Tôi không biết cách giảm thêm nữa, nhưng ai đó trên [Google Group] (https://groups.google.com/forum/?fromgroups#!forum/clojure) có thể. – JohnJ

4

Sử dụng tương lai rất dễ dàng trong Clojure. Ở bất kỳ mức nào, đây là câu trả lời giúp tránh cho họ

(defn conc [& fns] 
    (doall (pmap (fn [f] (f)) fns))) 

pmap sử dụng tương lai dưới mui xe. doall sẽ buộc trình tự đánh giá.

(let [[out1 out2] (conc fn1 fn2)] 
     [out1 out2]) 

Note, mà tôi destructured out1out2 trong một nỗ lực để bảo tồn ví dụ của bạn.

+0

để sử dụng điều này, bạn cần phải bọc đầu vào của bạn trong các chức năng vô danh tức là: # (fn1 42) –

+0

@ArthurUlfeldt đúng, trong trường hợp này tôi sẽ đi với giải pháp của JohnJ. –

+0

và tôi nghĩ rằng không gói nó trong một macro sẽ dẫn đến mã có thể kết hợp tốt hơn. Macro không phải là thứ hạng nhất, bạn không thể chuyển chúng sang ánh xạ hoặc áp dụng chúng vào danh sách các đối số. –

2

Tôi hiểu bạn không muốn giải pháp cuối cùng của bạn để lộ tương lai mặc dù nó rất hữu ích để minh họa làm thế nào để làm điều này với tương lai, và sau đó bọc chúng trong một cái gì đó mà giấu đi chi tiết này:

core> (defn fn1 [input] (java.lang.Thread/sleep 2000) (inc input)) 
#'core/fn1                      
core> (defn fn2 [input] (java.lang.Thread/sleep 3000) (* 2 input)) 
#'core/fn2                      
core> (time (let [f1 (future (fn1 4)) f2 (future (fn2 4))] @f1 @f2)) 
"Elapsed time: 3000.791021 msecs" 

sau đó chúng tôi có thể quấn nó lên trong bất kỳ bộ bao bọc clojure nào quanh tương lai. đơn giản nhất chỉ là một hàm có hai hàm và chạy chúng song song.

core> (defn conc [fn1 fn2] 
     (let [f1 (future (fn1)) 
       f2 (future (fn2))] [@f1 @f2])) 
#'core/conc                      
core> (time (conC#(fn1 4) #(fn2 4))) 
"Elapsed time: 3001.197634 msecs"                   

Điều này tránh sự cần thiết phải viết nó như là một vĩ mô bằng cách conc mất chức năng để chạy thay vì cơ thể để đánh giá, và sau đó tạo ra các chức năng để vượt qua nó bằng cách đặt # trước mặt của các cuộc gọi.

này cũng có thể được viết với bản đồ và tương lai gọi:

core> (map deref (map future-call [#(fn1 4) #(fn2 42)])) 
(5 84) 

Bạn có thể sau đó improce conc cho đến khi nó giống (như Julien Chastang một cách khôn ngoan chỉ ra) pmap