Mercurial > rlm
comparison src/rlm/function_utils.clj @ 0:78a630e650d2
initial import
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Tue, 18 Oct 2011 00:57:08 -0700 |
parents | |
children | 8565803376a4 c8e35134bf8e |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:78a630e650d2 |
---|---|
1 ;; Various Operators on Pure Functions | |
2 ;; | |
3 ;; Open source Liscence and all that | |
4 (ns | |
5 rlm.function-utils | |
6 "Collection of Operators on Pure Functions" | |
7 {:author "Robert McIntyre"} | |
8 (:use [clojure.contrib.profile])) | |
9 | |
10 (def void ::void) | |
11 | |
12 (comment | |
13 | |
14 "Please help me out here. I'm trying to make a higher order function that takes | |
15 pure functions that are mathmaticaly the same but have different times and returns another | |
16 function that runs them both and returns the resut of the one that finishes first." | |
17 | |
18 | |
19 "here's a repl session: | |
20 | |
21 mobius> " (time (dotimes [_ 500]((mix-futures + +) 40 3 3 3 3 3))) " | |
22 Elapsed time: 85.792995 msecs | |
23 nil | |
24 mobius> " (time (dotimes [_ 500](+ 40 3 3 3 3 3))) " | |
25 Elapsed time: 6.956338 msecs | |
26 nil | |
27 mobius> " (time (dotimes [_ 500]((mix-threads + +) 40 3 3 3 3 3))) " | |
28 Elapsed time: 706.227065 msecs | |
29 nil | |
30 | |
31 | |
32 mobius> " (defn fast-five [& args] 5) " | |
33 #'mobius/fast-five | |
34 | |
35 mobius> " (defn slow-five [& args] (Thread/sleep 5000) (println "Oh YEAH!!!!") 5) " | |
36 #'mobius/slow-five | |
37 | |
38 mobius> " (profile (time (dotimes [_ 5000] (thread-five)))) " | |
39 \"Elapsed time: 12187.981587 msecs\" | |
40 Name mean min max count sum | |
41 create-atom 9621 2025 4433928 5000 48106830 | |
42 create-threads 5156 2724 2880268 5000 25783796 | |
43 kill-threads 91313 27866 11175718 5000 456567066 | |
44 loop 2124249 38482 18470781 5000 10621249899 | |
45 return 1242 838 5309 5000 6212626 | |
46 start-threads 252985 110348 12272835 5000 1264927953 | |
47 nil | |
48 | |
49 mobius> " (profile (time (dotimes [_ 5000] (future-five)))) " | |
50 \"Elapsed time: 607.266671 msecs\" | |
51 Name mean min max count sum | |
52 create-atom 1472 1047 31708 5000 7363139 | |
53 create-futures 30539 2514 1330660 5000 152697998 | |
54 kill-threads 54158 2444 3938833 5000 270792897 | |
55 loop 81117 8800 6083059 5000 405587516 | |
56 return 1215 838 618782 5000 6078146 | |
57 nil | |
58 | |
59 | |
60 | |
61 What can I improve here, and why is the future version sooo much faster than the | |
62 thread version? Is there a better way than using loop? | |
63 " | |
64 | |
65 ) | |
66 | |
67 | |
68 (defn mix | |
69 "Takes any number of mathematically equal functions with | |
70 possibly different run-times and returns a function that | |
71 runs each in a separate thread, returns the result from | |
72 the first thread which finishes, and cancels the other threads." | |
73 {:author "Robert McIntyre"} | |
74 ([& functions] | |
75 (fn [& args] | |
76 (let [result (promise) | |
77 futures (doall (for [fun functions] | |
78 (future (deliver result (apply fun args))))) | |
79 answer @result] | |
80 (dorun (map future-cancel futures)) | |
81 answer)))) | |
82 | |
83 | |
84 | |
85 | |
86 (defn mix-threads | |
87 " Takes any number of pure functions that take the same arguments and | |
88 compute the same value and returns a function that runs each in a separate | |
89 thread, returns the result from the first thread which finshes, and cancels | |
90 the other threads. Explicitly uses nasty Threads. | |
91 | |
92 For example: | |
93 (do | |
94 (defn fun1 [] (Thread/sleep 5000) 5) | |
95 (defn fun2 [] (Thread/sleep 700000) 5) | |
96 (time ((mix fun1 fun2)))) | |
97 | |
98 Returns: | |
99 | Elapsed time: 5000.66214 msecs | |
100 5" | |
101 [& functions] | |
102 (fn [& args] | |
103 (let [result (prof :create-atom (atom void)) | |
104 threads | |
105 (prof :create-threads (map | |
106 (fn [fun] | |
107 (Thread. | |
108 (fn [] | |
109 (try (let [answer (apply fun args)] | |
110 (reset! result answer)) | |
111 (catch Exception _ nil))))) | |
112 functions))] | |
113 | |
114 (prof :start-threads (dorun (map #(.start %) threads))) | |
115 (prof :loop (loop [] | |
116 (if (= (deref result) void) | |
117 (recur) | |
118 (do (prof :kill-threads (dorun (map #(.stop %) threads))) | |
119 (prof :return (deref result))) | |
120 )))))) | |
121 | |
122 (defmacro defmix | |
123 " Defines a function from any number of pure functions that take the same | |
124 arguments and compute the same value which: | |
125 | |
126 Runs each in a separate thread. | |
127 Returns the result from the first thread which finshes. | |
128 Cancels the other threads. | |
129 | |
130 Use this whenever you want to combine two pure functions that | |
131 compute the same thing, but use different algorithms with different | |
132 run times for various inputs. | |
133 | |
134 For example: | |
135 (do | |
136 (defn fun1 [] (Thread/sleep 5000) 5) | |
137 (defn fun2 [] (Thread/sleep 700000) 5) | |
138 (defmix fun3 \"combination of fun1 and fun2\" fun1 fun2) | |
139 (time (fun3)) | |
140 | |
141 Returns: | |
142 | Elapsed time: 5000.66214 msecs | |
143 5" | |
144 | |
145 {:arglists '([name doc-string? functions*])} | |
146 | |
147 [name & functions] | |
148 (let [doc-string (if (string? (first functions)) (first functions) "") | |
149 functions (if (string? (first functions)) (rest functions) functions) | |
150 arglists (:arglists (meta (resolve (eval `(quote ~(first functions)))))) | |
151 name (with-meta name (assoc (meta name) :arglists `(quote ~arglists) :doc doc-string))] | |
152 `(def ~name (mix ~@functions)))) | |
153 | |
154 | |
155 (defn runonce | |
156 "Decorator. returns a function which will run only once. Inspired | |
157 by Halloway's version from lancet." | |
158 {:author "Robert McIntyre"} | |
159 [function] | |
160 (let [sentinel (Object.) | |
161 result (atom sentinel)] | |
162 (fn [& args] | |
163 (locking sentinel | |
164 (if (= @result sentinel) | |
165 (reset! result (apply function args)) | |
166 @result))))) | |
167 | |
168 | |
169 | |
170 ;I'm thinking this will be the docstring for mix eventually. | |
171 | |
172 ;; " Takes any number of pure functions that take the same arguments and | |
173 ;; compute the same value and returns a function that runs each in a separate | |
174 ;; thread, returns the result from the first thread which finshes, and cancels | |
175 ;; the other threads. | |
176 | |
177 ;; For example: | |
178 ;; (do | |
179 ;; (defn fun1 [] (Thread/sleep 5000) 5) | |
180 ;; (defn fun2 [] (Thread/sleep 700000) 5) | |
181 ;; (time ((mix fun1 fun2)))) | |
182 | |
183 ;; Returns: | |
184 ;; | Elapsed time: 5000.66214 msecs | |
185 ;; 5" |