clojure - Iteratively apply function to its result without generating a seq -
this 1 of "is there built-in/better/idiomatic/clever way this?" questions.
i want function--call fn-pow--that apply function f result of applying f argument, apply result of applying result, etc., n times. example,
(fn-pow inc 0 3) would equivalent to
(inc (inc (inc 0))) it's easy iterate:
(defn fn-pow-0 [f x n] (nth (iterate f x) n)) but creates , throws away unnecessary lazy sequence.
it's not hard write function scratch. here 1 version:
(defn fn-pow-1 [f x n] (if (> n 0) (recur f (f x) (dec n)) x)) i found twice fast fn-pow-0, using criterium on (fn-pow inc 0 10000000).
i don't consider definition of fn-pow-1 unidiomatic, fn-pow seems might standard built-in function, or there may simple way define couple of higher-order functions in clever arrangement. haven't succeeded in discovering either. missing something?
the built-in looking dotimes. i'll tell why in round-about fashion.
time
what testing in benchmark overhead of level of indirection. (nth (iterate ...) n) only twice slow compiles loop when body fast function rather surprising/encouraging. if f more costly function, importance of overhead diminishes. (of course if f low-level , fast, should use low-level loop construct.)
say function takes ~ 1 ms instead
(defn my-inc [x] (thread/sleep 1) (inc x)) then both of these take 1 second -- difference around 2% rather 100%.
(bench (fn-pow-0 my-inc 0 1000)) (bench (fn-pow-1 my-inc 0 1000)) space
the other concern iterate creating unnecessary sequence. but, if not holding onto head, doing nth, aren't really creating sequence per se sequentially creating, using, , discarding lazyseq objects. in other words, using constant amount of space, though generating garbage in proportion n. however, unless f primitive or mutating argument, already producing garbage in proportion n in producing own intermediate results.
reducing garbage
an interesting compromise between fn-pow-0 , fn-pow-1
(defn fn-pow-2 [f x n] (reduce (fn [x _] (f x)) x (range n))) since range objects know how intelligently reduce themselves, not create additional garbage in proportion n. boils down loop well. reduce method of range:
public object reduce(ifn f, object start) { object ret = f.invoke(start,n); for(int x = n+1;x < end;x++) ret = f.invoke(ret, x); return ret; } this fastest of 3 (before adding primitive type-hints on n in recur version, is) slowed down my-inc.
mutation
if iterating function potentially expensive in time or space, such matrix operations, may wanting use (in contained manner) f mutates argument eliminate garbage overhead. since mutation side effect, , want side effect n times, dotimes natural choice.
for sake of example, i'll use atom stand-in, imagine bashing on mutable matrix instead.
(def my-x (atom 0)) (defn my-inc! [x] (thread/sleep 1) (swap! x inc)) (defn fn-pow-3! [f! x n] (dotimes [i n] (f! x)))
Comments
Post a Comment