;;;; break and continue, with loop labels. ;;;; ;;;; The two things worth asserting here rather than in a unit test, because ;;;; they are about the code that comes out and not about the checker: ;;;; ;;;; 1. A (continue) in a dotimes still advances the counter. The step is the ;;;; loop's *latch* and not the last form of the body — folded onto the body ;;;; it would be jumped over and the program would hang, which is a test ;;;; that fails by never finishing rather than by printing the wrong thing. ;;;; The watchdog is what turns that back into a failure. ;;;; ;;;; 2. A labelled break leaves the loop it names and no other. (defstruct Hit [n i32]) (defn main [] i32 ;; break, unlabelled: the innermost loop. (let [i 0] (while (< i 100) (set i (+ i 1)) (when (= i 4) (break))) (print i) (println "")) ; 4 ;; continue in a while. The advance is written before it, because a while ;; has no latch of its own — that is the loop's own business and not the ;; compiler's. (let [j 0 seen 0] (while (< j 6) (set j (+ j 1)) (when (= (% j 2) 0) (continue)) (set seen (+ seen j))) (print seen) (println "")) ; 1 + 3 + 5 = 9 ;; continue in a dotimes, which is the one the latch exists for: the counter ;; must advance on the skipped iteration too, or this never returns. (let [sum 0] (dotimes [k 5] (when (= k 2) (continue)) (set sum (+ sum k))) (print sum) (println "")) ; 0 + 1 + 3 + 4 = 8 ;; A labelled break leaves the named loop. Without the label it would leave ;; the inner one and the outer would run all three times: 0 1 0 1 0 1. (dotimes :outer [a 3] (dotimes [b 3] (when (= b 2) (break :outer)) (print b) (println ""))) ; 0 1 ;; A labelled continue starts the *outer* loop's next iteration, so the rest ;; of the outer body is skipped as well as the rest of the inner one. (dotimes :rows [r 3] (dotimes [c 3] (when (= c 1) (continue :rows)) (print c) (println "")) (println "tail")) ; 0 0 0, and no tail ;; until takes a label on the same rule, being a while with a negated test. (let [n 0] (until :count (> n 10) (set n (+ n 1)) (when (= n 3) (break :count))) (print n) (println "")) ; 3 ;; A loop wholly inside a restart-case body has a perfectly good local ;; break: nothing the restart-case established is crossed by leaving a loop ;; that is inside it. This is the case the blanket refusal on return would ;; have caught and the relative rule does not. (let [t 0] (restart-case (while true (set t (+ t 1)) (when (> t 5) (break))) (carry-on [] (println "not reached"))) (print t) (println "")) ; 6 ;; The same the other way round: a handler-bind written inside the loop body ;; is entered and left before the break runs, so the break crosses nothing. (let [u 0] (while true (handler-bind [(Hit [c] (println "hit"))] (signal (Hit {.n 1}))) (set u (+ u 1)) (when (= u 2) (break))) (print u) (println "")) ; hit hit 2 0)