rlm@0
|
1 ;; Various Operators on Pure Functions
|
rlm@0
|
2 ;;
|
rlm@0
|
3 ;; Open source Liscence and all that
|
rlm@0
|
4 (ns
|
rlm@0
|
5 rlm.function-utils
|
rlm@0
|
6 "Collection of Operators on Pure Functions"
|
rlm@1
|
7 {:author "Robert McIntyre"})
|
rlm@0
|
8
|
rlm@0
|
9 (def void ::void)
|
rlm@0
|
10
|
rlm@6
|
11 (defn race
|
rlm@0
|
12 "Takes any number of mathematically equal functions with
|
rlm@0
|
13 possibly different run-times and returns a function that
|
rlm@0
|
14 runs each in a separate thread, returns the result from
|
rlm@0
|
15 the first thread which finishes, and cancels the other threads."
|
rlm@0
|
16 {:author "Robert McIntyre"}
|
rlm@0
|
17 ([& functions]
|
rlm@0
|
18 (fn [& args]
|
rlm@0
|
19 (let [result (promise)
|
rlm@0
|
20 futures (doall (for [fun functions]
|
rlm@0
|
21 (future (deliver result (apply fun args)))))
|
rlm@0
|
22 answer @result]
|
rlm@0
|
23 (dorun (map future-cancel futures))
|
rlm@0
|
24 answer))))
|
rlm@0
|
25
|
rlm@4
|
26 (defn race-pred
|
rlm@4
|
27 "Takes any number of mathematically equal functions with possibly
|
rlm@4
|
28 different run-times and returns a function that runs each in a
|
rlm@4
|
29 separate thread, and returns the first available result x for
|
rlm@4
|
30 which (pred x) returns true (or not-valid, if (pred x) returns
|
rlm@4
|
31 false on all the results). Cancels the other threads upon
|
rlm@4
|
32 returning early."
|
rlm@3
|
33 {:author "Robert McIntyre"}
|
rlm@4
|
34 ([pred not-valid & functions]
|
rlm@3
|
35 (fn [& args]
|
rlm@3
|
36 (let [result (promise)
|
rlm@4
|
37 latch (java.util.concurrent.CountDownLatch.
|
rlm@4
|
38 (count functions))
|
rlm@4
|
39 failure-case (future (.await latch)
|
rlm@4
|
40 (deliver result not-valid))
|
rlm@4
|
41 futures
|
rlm@4
|
42 (doall
|
rlm@4
|
43 (cons failure-case
|
rlm@4
|
44 (for [fun functions]
|
rlm@4
|
45 (future
|
rlm@4
|
46 (let [answer? (apply fun args)]
|
rlm@4
|
47 (if (pred answer?)
|
rlm@4
|
48 (deliver result answer?)
|
rlm@4
|
49 (.countDown latch)))))))
|
rlm@3
|
50 answer @result]
|
rlm@3
|
51 (dorun (map future-cancel futures))
|
rlm@3
|
52 answer))))
|
rlm@0
|
53
|
rlm@0
|
54 (defmacro defmix
|
rlm@0
|
55 " Defines a function from any number of pure functions that take the same
|
rlm@0
|
56 arguments and compute the same value which:
|
rlm@0
|
57
|
rlm@0
|
58 Runs each in a separate thread.
|
rlm@0
|
59 Returns the result from the first thread which finshes.
|
rlm@0
|
60 Cancels the other threads.
|
rlm@0
|
61
|
rlm@0
|
62 Use this whenever you want to combine two pure functions that
|
rlm@0
|
63 compute the same thing, but use different algorithms with different
|
rlm@0
|
64 run times for various inputs.
|
rlm@0
|
65
|
rlm@0
|
66 For example:
|
rlm@0
|
67 (do
|
rlm@0
|
68 (defn fun1 [] (Thread/sleep 5000) 5)
|
rlm@0
|
69 (defn fun2 [] (Thread/sleep 700000) 5)
|
rlm@0
|
70 (defmix fun3 \"combination of fun1 and fun2\" fun1 fun2)
|
rlm@0
|
71 (time (fun3))
|
rlm@0
|
72
|
rlm@0
|
73 Returns:
|
rlm@0
|
74 | Elapsed time: 5000.66214 msecs
|
rlm@0
|
75 5"
|
rlm@0
|
76
|
rlm@0
|
77 {:arglists '([name doc-string? functions*])}
|
rlm@0
|
78
|
rlm@0
|
79 [name & functions]
|
rlm@0
|
80 (let [doc-string (if (string? (first functions)) (first functions) "")
|
rlm@0
|
81 functions (if (string? (first functions)) (rest functions) functions)
|
rlm@0
|
82 arglists (:arglists (meta (resolve (eval `(quote ~(first functions))))))
|
rlm@1
|
83 name (with-meta name
|
rlm@1
|
84 (assoc (meta name) :arglists `(quote ~arglists)
|
rlm@1
|
85 :doc doc-string))]
|
rlm@0
|
86 `(def ~name (mix ~@functions))))
|
rlm@0
|
87
|
rlm@0
|
88 (defn runonce
|
rlm@0
|
89 "Decorator. returns a function which will run only once. Inspired
|
rlm@0
|
90 by Halloway's version from lancet."
|
rlm@0
|
91 {:author "Robert McIntyre"}
|
rlm@0
|
92 [function]
|
rlm@0
|
93 (let [sentinel (Object.)
|
rlm@0
|
94 result (atom sentinel)]
|
rlm@0
|
95 (fn [& args]
|
rlm@0
|
96 (locking sentinel
|
rlm@0
|
97 (if (= @result sentinel)
|
rlm@0
|
98 (reset! result (apply function args))
|
rlm@0
|
99 @result)))))
|
rlm@0
|
100
|