Mercurial > rlm
comparison src/rlm/function_utils.clj @ 1:8565803376a4
upgrading source to work with clojure 1.4
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Tue, 28 Feb 2012 13:26:34 -0600 |
parents | 78a630e650d2 |
children | b8bbb0dbda7b |
comparison
equal
deleted
inserted
replaced
0:78a630e650d2 | 1:8565803376a4 |
---|---|
2 ;; | 2 ;; |
3 ;; Open source Liscence and all that | 3 ;; Open source Liscence and all that |
4 (ns | 4 (ns |
5 rlm.function-utils | 5 rlm.function-utils |
6 "Collection of Operators on Pure Functions" | 6 "Collection of Operators on Pure Functions" |
7 {:author "Robert McIntyre"} | 7 {:author "Robert McIntyre"}) |
8 (:use [clojure.contrib.profile])) | |
9 | 8 |
10 (def void ::void) | 9 (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 | 10 |
68 (defn mix | 11 (defn mix |
69 "Takes any number of mathematically equal functions with | 12 "Takes any number of mathematically equal functions with |
70 possibly different run-times and returns a function that | 13 possibly different run-times and returns a function that |
71 runs each in a separate thread, returns the result from | 14 runs each in a separate thread, returns the result from |
78 (future (deliver result (apply fun args))))) | 21 (future (deliver result (apply fun args))))) |
79 answer @result] | 22 answer @result] |
80 (dorun (map future-cancel futures)) | 23 (dorun (map future-cancel futures)) |
81 answer)))) | 24 answer)))) |
82 | 25 |
26 ;; (defn mix-threads | |
27 ;; " Takes any number of pure functions that take the same arguments and | |
28 ;; compute the same value and returns a function that runs each in a separate | |
29 ;; thread, returns the result from the first thread which finshes, and cancels | |
30 ;; the other threads. Explicitly uses nasty Threads. | |
83 | 31 |
84 | 32 ;; For example: |
85 | 33 ;; (do |
86 (defn mix-threads | 34 ;; (defn fun1 [] (Thread/sleep 5000) 5) |
87 " Takes any number of pure functions that take the same arguments and | 35 ;; (defn fun2 [] (Thread/sleep 700000) 5) |
88 compute the same value and returns a function that runs each in a separate | 36 ;; (time ((mix fun1 fun2)))) |
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 | 37 |
98 Returns: | 38 ;; Returns: |
99 | Elapsed time: 5000.66214 msecs | 39 ;; | Elapsed time: 5000.66214 msecs |
100 5" | 40 ;; 5" |
101 [& functions] | 41 ;; [& functions] |
102 (fn [& args] | 42 ;; (fn [& args] |
103 (let [result (prof :create-atom (atom void)) | 43 ;; (let [result (prof :create-atom (atom void)) |
104 threads | 44 ;; threads |
105 (prof :create-threads (map | 45 ;; (prof :create-threads (map |
106 (fn [fun] | 46 ;; (fn [fun] |
107 (Thread. | 47 ;; (Thread. |
108 (fn [] | 48 ;; (fn [] |
109 (try (let [answer (apply fun args)] | 49 ;; (try (let [answer (apply fun args)] |
110 (reset! result answer)) | 50 ;; (reset! result answer)) |
111 (catch Exception _ nil))))) | 51 ;; (catch Exception _ nil))))) |
112 functions))] | 52 ;; functions))] |
113 | 53 |
114 (prof :start-threads (dorun (map #(.start %) threads))) | 54 ;; (prof :start-threads (dorun (map #(.start %) threads))) |
115 (prof :loop (loop [] | 55 ;; (prof :loop (loop [] |
116 (if (= (deref result) void) | 56 ;; (if (= (deref result) void) |
117 (recur) | 57 ;; (recur) |
118 (do (prof :kill-threads (dorun (map #(.stop %) threads))) | 58 ;; (do (prof :kill-threads (dorun (map #(.stop %) threads))) |
119 (prof :return (deref result))) | 59 ;; (prof :return (deref result))))))))) |
120 )))))) | |
121 | 60 |
122 (defmacro defmix | 61 (defmacro defmix |
123 " Defines a function from any number of pure functions that take the same | 62 " Defines a function from any number of pure functions that take the same |
124 arguments and compute the same value which: | 63 arguments and compute the same value which: |
125 | 64 |
146 | 85 |
147 [name & functions] | 86 [name & functions] |
148 (let [doc-string (if (string? (first functions)) (first functions) "") | 87 (let [doc-string (if (string? (first functions)) (first functions) "") |
149 functions (if (string? (first functions)) (rest functions) functions) | 88 functions (if (string? (first functions)) (rest functions) functions) |
150 arglists (:arglists (meta (resolve (eval `(quote ~(first functions)))))) | 89 arglists (:arglists (meta (resolve (eval `(quote ~(first functions)))))) |
151 name (with-meta name (assoc (meta name) :arglists `(quote ~arglists) :doc doc-string))] | 90 name (with-meta name |
91 (assoc (meta name) :arglists `(quote ~arglists) | |
92 :doc doc-string))] | |
152 `(def ~name (mix ~@functions)))) | 93 `(def ~name (mix ~@functions)))) |
153 | |
154 | 94 |
155 (defn runonce | 95 (defn runonce |
156 "Decorator. returns a function which will run only once. Inspired | 96 "Decorator. returns a function which will run only once. Inspired |
157 by Halloway's version from lancet." | 97 by Halloway's version from lancet." |
158 {:author "Robert McIntyre"} | 98 {:author "Robert McIntyre"} |
163 (locking sentinel | 103 (locking sentinel |
164 (if (= @result sentinel) | 104 (if (= @result sentinel) |
165 (reset! result (apply function args)) | 105 (reset! result (apply function args)) |
166 @result))))) | 106 @result))))) |
167 | 107 |
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" |