rlm@0: ;; Various Operators on Pure Functions rlm@0: ;; rlm@0: ;; Open source Liscence and all that rlm@0: (ns rlm@0: rlm.function-utils rlm@0: "Collection of Operators on Pure Functions" rlm@1: {:author "Robert McIntyre"}) rlm@0: rlm@0: (def void ::void) rlm@0: rlm@0: (defn mix rlm@0: "Takes any number of mathematically equal functions with rlm@0: possibly different run-times and returns a function that rlm@0: runs each in a separate thread, returns the result from rlm@0: the first thread which finishes, and cancels the other threads." rlm@0: {:author "Robert McIntyre"} rlm@0: ([& functions] rlm@0: (fn [& args] rlm@0: (let [result (promise) rlm@0: futures (doall (for [fun functions] rlm@0: (future (deliver result (apply fun args))))) rlm@0: answer @result] rlm@0: (dorun (map future-cancel futures)) rlm@0: answer)))) rlm@0: rlm@1: ;; (defn mix-threads rlm@1: ;; " Takes any number of pure functions that take the same arguments and rlm@1: ;; compute the same value and returns a function that runs each in a separate rlm@1: ;; thread, returns the result from the first thread which finshes, and cancels rlm@1: ;; the other threads. Explicitly uses nasty Threads. rlm@0: rlm@1: ;; For example: rlm@1: ;; (do rlm@1: ;; (defn fun1 [] (Thread/sleep 5000) 5) rlm@1: ;; (defn fun2 [] (Thread/sleep 700000) 5) rlm@1: ;; (time ((mix fun1 fun2)))) rlm@0: rlm@1: ;; Returns: rlm@1: ;; | Elapsed time: 5000.66214 msecs rlm@1: ;; 5" rlm@1: ;; [& functions] rlm@1: ;; (fn [& args] rlm@1: ;; (let [result (prof :create-atom (atom void)) rlm@1: ;; threads rlm@1: ;; (prof :create-threads (map rlm@1: ;; (fn [fun] rlm@1: ;; (Thread. rlm@1: ;; (fn [] rlm@1: ;; (try (let [answer (apply fun args)] rlm@1: ;; (reset! result answer)) rlm@1: ;; (catch Exception _ nil))))) rlm@1: ;; functions))] rlm@0: rlm@1: ;; (prof :start-threads (dorun (map #(.start %) threads))) rlm@1: ;; (prof :loop (loop [] rlm@1: ;; (if (= (deref result) void) rlm@1: ;; (recur) rlm@1: ;; (do (prof :kill-threads (dorun (map #(.stop %) threads))) rlm@1: ;; (prof :return (deref result))))))))) rlm@0: rlm@0: (defmacro defmix rlm@0: " Defines a function from any number of pure functions that take the same rlm@0: arguments and compute the same value which: rlm@0: rlm@0: Runs each in a separate thread. rlm@0: Returns the result from the first thread which finshes. rlm@0: Cancels the other threads. rlm@0: rlm@0: Use this whenever you want to combine two pure functions that rlm@0: compute the same thing, but use different algorithms with different rlm@0: run times for various inputs. rlm@0: rlm@0: For example: rlm@0: (do rlm@0: (defn fun1 [] (Thread/sleep 5000) 5) rlm@0: (defn fun2 [] (Thread/sleep 700000) 5) rlm@0: (defmix fun3 \"combination of fun1 and fun2\" fun1 fun2) rlm@0: (time (fun3)) rlm@0: rlm@0: Returns: rlm@0: | Elapsed time: 5000.66214 msecs rlm@0: 5" rlm@0: rlm@0: {:arglists '([name doc-string? functions*])} rlm@0: rlm@0: [name & functions] rlm@0: (let [doc-string (if (string? (first functions)) (first functions) "") rlm@0: functions (if (string? (first functions)) (rest functions) functions) rlm@0: arglists (:arglists (meta (resolve (eval `(quote ~(first functions)))))) rlm@1: name (with-meta name rlm@1: (assoc (meta name) :arglists `(quote ~arglists) rlm@1: :doc doc-string))] rlm@0: `(def ~name (mix ~@functions)))) rlm@0: rlm@0: (defn runonce rlm@0: "Decorator. returns a function which will run only once. Inspired rlm@0: by Halloway's version from lancet." rlm@0: {:author "Robert McIntyre"} rlm@0: [function] rlm@0: (let [sentinel (Object.) rlm@0: result (atom sentinel)] rlm@0: (fn [& args] rlm@0: (locking sentinel rlm@0: (if (= @result sentinel) rlm@0: (reset! result (apply function args)) rlm@0: @result))))) rlm@0: