;;;; dotimes with start, stop and step. ;;;; ;;;; stop is exclusive in every arity, so (dotimes [i 0 n]) is (dotimes [i n]), ;;;; and a negative step counts down and tests with > instead of <. What is ;;;; asserted here rather than in a unit test is what comes out: ;;;; ;;;; 1. Each bound is evaluated exactly once, before the loop, and left to ;;;; right. The counter function prints a marker per call, so a bound read ;;;; twice shows up as an extra marker and a bound read per iteration shows ;;;; up as many. ;;;; ;;;; 2. A body that assigns to what a bound was computed from cannot change the ;;;; trip count — the bounds are in hidden slots by then. ;;;; ;;;; 3. (continue) advances a *down*-counting loop too. The step is the latch ;;;; whichever way it goes, and folded onto the body this hangs rather than ;;;; printing the wrong thing, which is what the watchdog is for. ;;;; ;;;; 4. A step whose sign is only known at run time picks its direction at the ;;;; test, and a run-time step of 0 runs the loop no times at all. ;; Prints its tag and answers with its value, so the output says how many times ;; and in what order each bound was evaluated. (defn bump [tag i32 v i32] i32 (print tag) v) (defn main [] i32 ;; ── the three arities, counting up ──────────────────────────────── (dotimes [i 3] (print i)) (println "") ; 012 (dotimes [i 2 5] (print i)) (println "") ; 234 (dotimes [i 0 10 3] (print i)) (println "") ; 0369 — uneven, stops short ;; (dotimes [i 0 n]) is (dotimes [i n]). One rule, not two. (dotimes [i 0 4] (print i)) (println "") ; 0123 ;; ── counting down ───────────────────────────────────────────────── (dotimes [i 9 -1 -1] (print i)) (println "") ; 9876543210 (dotimes [i 10 0 -3] (print i)) (println "") ; 10 7 4 1 ;; The last representable i32 is reachable as a stop: it is exclusive, so ;; the counter reaches -2147483647 and the test ends it there. (dotimes [i -2147483645 -2147483648 -1] (print i) (println "")) ; three lines ;; ── zero-trip loops ─────────────────────────────────────────────── (dotimes [i 5 5] (print i)) ; nothing (dotimes [i 0 10 -1] (print i)) ; nothing: already past (dotimes [i 10 0] (print i)) ; nothing: already past (println "none") ;; ── each bound once, in the order written ───────────────────────── ;; 7 8 9 for the three bounds, then the two iterations. A bound evaluated ;; per iteration would interleave; one evaluated twice would repeat. (dotimes [i (bump 7 0) (bump 8 2) (bump 9 1)] (print i)) (println "") ;; The same for the one-bound form, which is the one that always worked. (dotimes [i (bump 7 2)] (print i)) (println "") ;; ── a body cannot move the bounds ───────────────────────────────── (let [n 3] (dotimes [i 0 n] (set n 0) (print i))) ; 012, not 0 (println "") (let [s 1] (dotimes [i 0 3 s] (set s 5) (print i))) ; 012, not 0 (println "") ;; ── break and continue, in every arity ──────────────────────────── (dotimes [i 5] (when (= i 3) (break)) (print i)) (println "") ; 012 (dotimes [i 5] (when (= i 2) (continue)) (print i)) (println "") ; 0134 (dotimes [i 2 8] (when (= i 5) (break)) (print i)) (println "") ; 234 (dotimes [i 2 6] (when (= i 4) (continue)) (print i)) (println "") ; 235 (dotimes [i 0 12 3] (when (= i 9) (break)) (print i)) (println "") ; 036 (dotimes [i 0 12 3] (when (= i 6) (continue)) (print i)) (println "") ; 039 ;; Counting down, which is the new path for the latch: the skipped ;; iteration still subtracts, or this never finishes. (dotimes [i 5 0 -1] (when (= i 3) (break)) (print i)) (println "") ; 54 (dotimes [i 5 0 -1] (when (= i 3) (continue)) (print i)) (println "") ; 5421 ;; Every iteration continues and the trip count is still the trip count. (let [c 0] (dotimes [i 4 0 -1] (set c (+ c 1)) (continue)) (print c)) (println "") ; 4 ;; Labels, on a down-counting loop. (dotimes :outer [a 2 -1 -1] (dotimes [b 2 -1 -1] (when (= b 0) (break :outer)) (print b))) (println "") ; 21 (dotimes :rows [r 2 -1 -1] (dotimes [c 2 -1 -1] (when (= c 1) (continue :rows)) (print c)) (println "tail")) (println "") ; 222, no tail ;; ── a step the compiler cannot see the sign of ──────────────────── (let [up 2 down -2 flat 0] (dotimes [i 0 7 up] (print i)) ; 0246 (println "") (dotimes [i 6 -1 down] (print i)) ; 6420 (println "") ;; A step of 0 cannot be refused here — it is a value, not a literal — so ;; the sign test leaves it with no direction and the loop runs no times. (dotimes [i 0 7 flat] (print i)) (println "zero") ;; And it is evaluated once like the others, so a body that changes it ;; changes nothing. (dotimes [i (bump 4 0) (bump 5 6) (bump 6 up)] (print i)) (println "")) 0)