Mercurial > rlm
view src/rlm/function_utils.clj @ 4:12d1367cf1aa
updating various utilities
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Thu, 01 Mar 2012 05:47:23 -0700 |
parents | c8e35134bf8e |
children | b8bbb0dbda7b |
line wrap: on
line source
1 ;; Various Operators on Pure Functions2 ;;3 ;; Open source Liscence and all that4 (ns5 rlm.function-utils6 "Collection of Operators on Pure Functions"7 {:author "Robert McIntyre"}8 (:use [clojure.contrib.profile]))10 (def void ::void)12 (comment14 "Please help me out here. I'm trying to make a higher order function that takes15 pure functions that are mathmaticaly the same but have different times and returns another16 function that runs them both and returns the resut of the one that finishes first."19 "here's a repl session:21 mobius> " (time (dotimes [_ 500]((mix-futures + +) 40 3 3 3 3 3))) "22 Elapsed time: 85.792995 msecs23 nil24 mobius> " (time (dotimes [_ 500](+ 40 3 3 3 3 3))) "25 Elapsed time: 6.956338 msecs26 nil27 mobius> " (time (dotimes [_ 500]((mix-threads + +) 40 3 3 3 3 3))) "28 Elapsed time: 706.227065 msecs29 nil32 mobius> " (defn fast-five [& args] 5) "33 #'mobius/fast-five35 mobius> " (defn slow-five [& args] (Thread/sleep 5000) (println "Oh YEAH!!!!") 5) "36 #'mobius/slow-five38 mobius> " (profile (time (dotimes [_ 5000] (thread-five)))) "39 \"Elapsed time: 12187.981587 msecs\"40 Name mean min max count sum41 create-atom 9621 2025 4433928 5000 4810683042 create-threads 5156 2724 2880268 5000 2578379643 kill-threads 91313 27866 11175718 5000 45656706644 loop 2124249 38482 18470781 5000 1062124989945 return 1242 838 5309 5000 621262646 start-threads 252985 110348 12272835 5000 126492795347 nil49 mobius> " (profile (time (dotimes [_ 5000] (future-five)))) "50 \"Elapsed time: 607.266671 msecs\"51 Name mean min max count sum52 create-atom 1472 1047 31708 5000 736313953 create-futures 30539 2514 1330660 5000 15269799854 kill-threads 54158 2444 3938833 5000 27079289755 loop 81117 8800 6083059 5000 40558751656 return 1215 838 618782 5000 607814657 nil61 What can I improve here, and why is the future version sooo much faster than the62 thread version? Is there a better way than using loop?63 "65 )68 (defn race69 "Takes any number of mathematically equal functions with70 possibly different run-times and returns a function that71 runs each in a separate thread, returns the result from72 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))))83 (defn race-pred84 "Takes any number of mathematically equal functions with possibly85 different run-times and returns a function that runs each in a86 separate thread, and returns the first available result x for87 which (pred x) returns true (or not-valid, if (pred x) returns88 false on all the results). Cancels the other threads upon89 returning early."90 {:author "Robert McIntyre"}91 ([pred not-valid & functions]92 (fn [& args]93 (let [result (promise)94 latch (java.util.concurrent.CountDownLatch.95 (count functions))96 failure-case (future (.await latch)97 (deliver result not-valid))98 futures99 (doall100 (cons failure-case101 (for [fun functions]102 (future103 (let [answer? (apply fun args)]104 (if (pred answer?)105 (deliver result answer?)106 (.countDown latch)))))))107 answer @result]108 (dorun (map future-cancel futures))109 answer))))113 (defn mix-threads114 " Takes any number of pure functions that take the same arguments and115 compute the same value and returns a function that runs each in a separate116 thread, returns the result from the first thread which finshes, and cancels117 the other threads. Explicitly uses nasty Threads.119 For example:120 (do121 (defn fun1 [] (Thread/sleep 5000) 5)122 (defn fun2 [] (Thread/sleep 700000) 5)123 (time ((mix fun1 fun2))))125 Returns:126 | Elapsed time: 5000.66214 msecs127 5"128 [& functions]129 (fn [& args]130 (let [result (prof :create-atom (atom void))131 threads132 (prof :create-threads (map133 (fn [fun]134 (Thread.135 (fn []136 (try (let [answer (apply fun args)]137 (reset! result answer))138 (catch Exception _ nil)))))139 functions))]141 (prof :start-threads (dorun (map #(.start %) threads)))142 (prof :loop (loop []143 (if (= (deref result) void)144 (recur)145 (do (prof :kill-threads (dorun (map #(.stop %) threads)))146 (prof :return (deref result)))147 ))))))149 (defmacro defmix150 " Defines a function from any number of pure functions that take the same151 arguments and compute the same value which:153 Runs each in a separate thread.154 Returns the result from the first thread which finshes.155 Cancels the other threads.157 Use this whenever you want to combine two pure functions that158 compute the same thing, but use different algorithms with different159 run times for various inputs.161 For example:162 (do163 (defn fun1 [] (Thread/sleep 5000) 5)164 (defn fun2 [] (Thread/sleep 700000) 5)165 (defmix fun3 \"combination of fun1 and fun2\" fun1 fun2)166 (time (fun3))168 Returns:169 | Elapsed time: 5000.66214 msecs170 5"172 {:arglists '([name doc-string? functions*])}174 [name & functions]175 (let [doc-string (if (string? (first functions)) (first functions) "")176 functions (if (string? (first functions)) (rest functions) functions)177 arglists (:arglists (meta (resolve (eval `(quote ~(first functions))))))178 name (with-meta name (assoc (meta name) :arglists `(quote ~arglists) :doc doc-string))]179 `(def ~name (mix ~@functions))))182 (defn runonce183 "Decorator. returns a function which will run only once. Inspired184 by Halloway's version from lancet."185 {:author "Robert McIntyre"}186 [function]187 (let [sentinel (Object.)188 result (atom sentinel)]189 (fn [& args]190 (locking sentinel191 (if (= @result sentinel)192 (reset! result (apply function args))193 @result)))))197 ;I'm thinking this will be the docstring for mix eventually.199 ;; " Takes any number of pure functions that take the same arguments and200 ;; compute the same value and returns a function that runs each in a separate201 ;; thread, returns the result from the first thread which finshes, and cancels202 ;; the other threads.204 ;; For example:205 ;; (do206 ;; (defn fun1 [] (Thread/sleep 5000) 5)207 ;; (defn fun2 [] (Thread/sleep 700000) 5)208 ;; (time ((mix fun1 fun2))))210 ;; Returns:211 ;; | Elapsed time: 5000.66214 msecs212 ;; 5"