Introduction to Code-Golf in Clojure
Code-Golf is the art of writing the shortest program in a given language that implements some given algorithm. It started in the 90’s in the Perl community and spread to other languages; there are now languages dedicated to code-golfing and StackExchange has a Q&A website for it.
In 2015, for example, I wrote a blog post showing how to write a JavaScript modules manager that fits in 140 chars (the maximum length of a tweet at that time).
4clojure is a well-known website to learn Clojure through exercises of increasing difficulty, but it has a lesser-known code-golf challenge which you can enable by clicking on “Leagues” in the top menu. If you check the code-golf checkbox, you then get a score on each problem that is the number of non-whitespace characters of your solution; the smaller the better.
The first thing you’ll note when code-golfing is that the reader syntax for
anonymous functions is a lot shorter than using fn
:
; 18 chars
(fn [a b c] (* (+ a b) c))
; 13 chars
#(* (+ %1 %2) %3)
; 12 chars: -1 char because '%' is equivalent to '%1'
#(* (+ % %2) %3)
Unfortunately you can’t have a reader-syntax function inside another reader-syntax one, so you often have to transform the code not to use anonymous functions.
for
is a very powerful tool for that, because it allows you to do the
equivalent of map
, and a lot more, with no function:
; invalid!
#(map #(* 2 %) %)
; 19 chars
#(map (fn [x] (* 2 x)) %)
; 17 chars
#(map (partial * 2) %)
; 15 chars
#(for [x %] (* 2 x))
; Note that for this specific example
; the best solution uses `map`:
#(map + % %)
On some problems it can even be shorter than using map
+ filter
:
; 31 chars
(fn [x a]
(map inc (filter #(< % a) x)))
; 26 chars
#(for [e x :when (< e a)] (inc e))
Some core functions are equivalent in some contexts and so the shorter one can substitute a longer one:
; 18 chars
#(filter identity %)
; 14 chars
#(filter comp %)
; 6 chars
(inc x)
(dec x)
; 5 chars
(+ x 1)
(- x 1)
; 12 chars
(reduce str x)
; 11 chars
(apply str x)
; 14 chars
(apply concat x)
; 13 chars
(mapcat comp x)
When you must use a long function name in multiple places, it might be shorter
to let
that function with a one-letter symbol:
; 120 chars
#(clojure.set/difference
(clojure.set/union % %2)
(clojure.set/union
(clojure.set/difference % %2)
(clojure.set/difference %2 %)))
; 73 chars
#(let [d clojure.set/difference u clojure.set/union]
(d (u % %2) (u (d % %2) (d %2 %))))
; Note that for this specific example
; there is a 17-chars solution
#(set (filter %2 %))
Other tricks
Use indexed access on vectors:
; 15 chars
(first [:a :b :c])
; 11 chars
([:a :b :c] 0)
Use set literals as functions:
; 16 chars
(remove #(= :a %) x)
; 14 chars
(remove #{:a} x)
Inverse conditions to use shorter functions:
; 15 chars
(if (empty? p) a b)
; 12 chars
(if (seq p) b a)
Inlined code is sometimes shorter:
; 24 chars
(let [p (* 3 a)]
(if (< p 5)
a
p))
; 19 chars
(if (< (* 3 a) 5)
a
(* 3 a))
Use 1
instead of :else
/:default
in cond
:
; 24 chars
(cond
(= m p) a
(< m p) b
:else c)
; 20 chars
(cond
(= m p) a
(< m p) b
1 c)
Use maps instead of if
s for conditions on equality (this one really makes
the code harder to read):
; 13 chars
(if (= "L" x) a b)
; 12 chars
(case x "L" a b)
; 10 chars
({"L" a} x b)