6

独立した2つのコストのかかる関数があります。それらを並行して実行したいと思います。私は先物などを扱いたくありません(私はClojureに不慣れで、簡単に混乱します)。

2つの関数を同時に実行する簡単な方法を探しています。次のように動作させたい

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

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

これが出力のペアを持つベクトルを返すようにしたいです。両方のスレッドが終了した場合にのみ戻る必要があります。理想的には、concは任意の数の入力に対して機能するはずです。これは単純なパターンだと思います。

4

3 に答える 3

4

Clojureでは先物の使用は非常に簡単です。とにかく、ここにそれらを回避する答えがあります

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

pmap内部で先物を使用します。doallシーケンスを強制的に評価します。

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

私は構造を解除し、あなたの例を維持しようout1としていることに注意してください。out2

于 2012-09-13T22:48:03.900 に答える
3

他の回答が示すように、同じ動作を取得する他の方法がありますが、目的の構文を保持するためにマクロが必要です。これを行う1つの方法は次のとおりです。

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

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

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

拡張は、それがどのように機能するかを示しています。

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

2つの関数を指定しましたが、これは任意の数の式で機能するはずです。

于 2012-09-14T00:10:13.230 に答える
2

先物でこれを行う方法を説明し、この詳細を隠すものでそれらをラップすることは有用ですが、あなたが最終的な解決策で先物を公開することを望まないことを理解しています。

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"  

次に、先物の周りの多くのclojureラッパーのいずれかでそれをラップできます。最も単純なのは、2つの関数を受け取り、それらを並行して実行する関数です。

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"                                                                          

#これにより、concが評価する本体の代わりに実行する関数を取得し、呼び出しの前に 置くことによってそれに渡す関数を作成することにより、マクロとして記述する必要がなくなります。

これは、mapとfuture-callを使用して作成することもできます。

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

次に、concを次のようになるまでimproceできます(Julien Chastangが賢明に指摘しているように)pmap

于 2012-09-13T22:49:22.730 に答える