Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cross between "dotimes" and "for" functionality?

I frequently find myself wanting to efficiently run a Clojure function multiple times with an integer index (like "dotimes") but also get the results out as a ready-made sequence/list (like "for").

i.e. I'd like to do something like this:

(fortimes [i 10] (* i i))

=> (0 1 4 9 16 25 36 49 64 81)

Clearly it would be possible to do:

(for [i (range 10)] (* i i))

But I'd like to avoid creating and throwing away the temporary range list if at all possible.

What's the best way to achieve this in Clojure?

like image 753
mikera Avatar asked Dec 27 '10 15:12

mikera


1 Answers

Generating a range in a for loop, as you show in your second example, is the idiomatic solution for solving this problem in Clojure.

Since Clojure is grounded in the functional paradigm, programming in Clojure, by default, will generate temporary data structures like this. However, since both the "range" and the "for" command operate with lazy sequences, writing this code does not force the entire temporary range data structure to exist in memory at once. If used properly, there is therefore a very low memory overhead for lazy seqs as used in this example. Also, the computational overhead for your example is modest and should only grow linearly with the size of the range. This is considered an acceptable overhead for typical Clojure code.

The appropriate way to completely avoid this overhead, if the temporary range list is absolutely, positively unacceptable for your situation, is to write your code using atoms or transients: http://clojure.org/transients. It you do this, however, you will give up many of the advantages of the Clojure programming model in exchange for slightly better performance.

like image 141
drcode Avatar answered Sep 30 '22 11:09

drcode