dotimes repeats a body a specified number of times for side effects. Its binding vector supplies a local integer and a repetition count; the body may contain any number of expressions, and the macro always returns nil. section 3.4.5
The contract is:
- Use exactly two binding-vector elements: [local n]. section 3.4.5
- Evaluate the body with local starting at zero and incrementing by one. section 3.4.5
- Skip the body when n is zero or negative. section 3.4.5
- Return nil rather than collected results, making side effects the primary use. section 3.4.5
Printing indexed iterations
(dotimes [i 3] (println i)) ;; 0 ;; 1 ;; 2 ;; nil The index is also useful for repeated array access or transient-vector updates. For example, a transient result can be filled in place with (assoc! res i value), then converted with persistent!. section 3.4.5
Measuring repeated work
(time (dotimes [_ 1000000] (apply max (range 100)))) ;; "Elapsed time: ... msecs" ;; nil The ignored binding communicates that the iteration number is unnecessary. dotimes provides a quick timing loop, while more rigorous benchmarking can use Criterium. section 3.4.5
(macroexpand '(dotimes [i 3] (println i))) ;; (let* [n (long 3)] ;; (loop [i 0] ;; (when (< i n) ;; (println i) ;; (recur (unchecked-inc i)))))
The macro expands into a let*, loop, when, and recur, revealing a counted loop with an unchecked increment. section 3.4.5
Choose dotimes when computation exists for its effects and no results need collecting. Use sequence-oriented alternatives such as repeatedly when generated values are the result, or doseq when iterating over an existing collection. section 9.2.1 section 3.4.5
