Repeating yourself in Clojure
Clojure has a number of ways to repeat actions or sequences, but sometimes it can be a little confusing to know which to pick, or to remember all of the options. This post will discuss repeat, cycle, iterate, repeatedly, constantly, and dotimes. From the names, you can tell they all involve repetition, but beyond that it’s not obvious at first glance what they do. Lets dive in!
Repeat
Repeat seems like the first tool in the bag when you want to repeat something. The docstring says “Returns a lazy (infinite!, or length n if supplied) sequence of xs.” It has two arities, [x], and [n x]. If you don’t supply a length, make sure to terminate the sequence at some point with a take or another function which will not consume the entire (infinite) sequence.
Lets try it out:
(repeat 5 :a)
;; (:a :a :a :a :a)
(repeat :a)
;; Operation infinite exception
(take 5 (repeat :a))
;; (:a :a :a :a :a)
(repeat 5 [:a :b :c])
;; ([:a :b :c] [:a :b :c] ...)
That last one might not have been what you were expecting. To repeat each element in a collection you need to use…
Cycle
Cycle will return a “lazy (infinite!) sequence of repetitions of the items in coll.” This is what we wanted in our last example. Cycle doesn’t have an option to take a length, so we need to wrap it in take or it will carry on forever.
(take 5 (cycle [:a :b :c]))
;; (:a :b :c :a :b)
Iterate
Iterate is another lazy, infinite function that needs to be guarded with take. It takes a function and a starting value. It evaluates (f x), then calls f on the result of (f x), then calls f on the result of that result, and so on ad infinitum. The canonical example for this is generating arithmetic sequences.
(take 5 (iterate inc 3))
;; (3 4 5 6 7)
It’s not often seen in real world code, as evidenced by most of the GitHub search results for iterate being for solving 4Clojure problems.
Repeatedly
The documentation for repeatedly says:
Takes a function of no args, presumably with side effects, and returns an infinite (or length n if supplied) lazy sequence of calls to it
Clojure generally tries to avoid side effecting functions, so this one doesn’t get so much air time, but one usage is for generating a sequence of random numbers. Again as an infinite sequence it needs to be guarded with a take.
(take 5 (repeatedly (fn [] (rand-int 10))))
;; (6 2 5 2 1)
Constantly
Constantly takes a value, and returns a function which will always return that value. The function takes any number of arguments.
(def bfun (constantly :b))
;; #'user/bfun
(bfun)
;; :b
(bfun :lots :of :args)
;; :b
This is often used when you have a constant that you always want to use, but the API that you’re working with requires you to pass it a function, not a value. A real world example is in Reid McKenzie’s Pascal parser. I often get repeatedly
and constantly
confused, so don’t feel bad if you need to look up the documentation when you come across one of them!
Dotimes
Dotimes is the black sheep of the repeating function family, it’s the only one to use a binding expression. It returns nil, so it’s only really useful for side effecting functions. You give it a binding expression with a name
and the number of repetitions you want, and it will execute the function with name
bound to the repetition number. It starts counting from 0 and goes to repetition number - 1.
For example:
(dotimes [i 5]
i)
;; nil
;; This doesn't have any side effects and we are just returned nil
(dotimes [i 5]
(println i))
0
1
2
3
4
;; nil
;; i is printed to the console each time through the loop