Code Golf in Clojure

Posted: September 21, 2012 in Uncategorized
Tags: , ,

Recently enjoyed doing a bit of code golf – in particular solving the FizzBuzz test, which I managed to get down to 80 characters in Clojure (let me know if you can do better!)


(map #(let[t(mod% 3)z{0"Fizz"}]({0(str(z t)'Buzz)}(mod% 5)(z t%)))(range 1 101))

The experience inspired me to write down and share a few tips and tricks….

;; ===============================================
;; General / basic tricks

;; whitespace is optional in clojure unless needed to delimit symbols
;; spaces around all types of parentheses etc. can be removed
(for [i (range 10)] {:key i :value "foo"})
(for[i(range 10)]{:key i :value"foo"})

;; ===============================================
;; Strings

;; strings can often be used directly as a sequence of characters
;; - without needing "seq"
(reverse (seq "Abracadabra"))
(reverse "Abracadabra")

;; strings used with maps are useful to generate long sequences
(let[a "Eggs"b"Jelly"c"Toast"][a b c a a b b c c a a a b b b c c c])
(map{\a"Eggs"\b"Jelly"\c"Toast"}"abcaabbccaaabbbccc")

;; ==============================================
;; Anonymous functions

;; you can still use % in ananoymous function with multiple arguments
(#(str %1 %3 %2) "A" "B" "C")
(#(str % %3 %2) "A" "B" "C")

;; furthermore, spaces can be removed from ananoymous functions
(#(str % %3 %2) "A" "B" "C")
(#(str%%3%2) "A" "B" "C")

;; map with an anonymous function is shorter than for in many cases
(for[i(range 10)](* i i))
(map #(*%%)(range 10))

;; ======================================================
;; Literals and constants

;; scientific notation can shorten certain common large constants
1000000
1E6

;; set literals are useful for filtering
(filter #(= "Foo" %) ["Foo" "Bar" "Baz"])
(filter #{"Foo"} ["Foo" "Bar" "Baz"])

;; hashmaps are equivalent to and shorter than sequences of pairs
;; when used as a sequence
(seq [["A"1]["B"2]["C"3]])
(seq {"A"1"B"2"C"3})
;; ======================================================
;; Better versions of common functions

;; apply can be shorter than reduce
(reduce + (range 1000))
(apply + (range 1000))

;; #(do%) is shorter than identity
(partition-by identity [1 1 1 2 3 3 3 3 6 6 6 6])
(partition-by #(do%) [1 1 1 2 3 3 3 3 6 6 6 6])

;; indexed access on vectors is shorter than first (or second etc..)
(first [:a :b])
([:a :b] 0)

;; ======================================================
;; Unusual functions good for golfing

;; frequencies is a long function name, but is surprisingly useful
;; e.g. getting the most common character in a string
((last(sort-by second(frequencies "abracadabra")))0)

;; ======================================================
;; Advanced tricks

;; adding a single-element vector to a for loop can be shorter
;; when compared to repeating a literal or a let
(for [i (range 10)] [(str "A long-string needed twice" i) "A long-string needed twice"])
(for [i (range 10)] (let [s "A long-string needed twice"] [(str s i) s]))
(for [i (range 10) s ["A long-string needed twice"]] [(str s i) s])

;; if they are goimg to be converted to strings, using symbols
;; can be shorter than quoted text
(map #(str "foo" %) ["A" "B" "C"])
(map #(str "foo" %) ['A 'B 'C])

;; if you use a function repeatedly, it may be better to alias it
(for [i (range 100)] [(mod i 3) (mod i 5) (mod i 7) (mod i 9)])
(for [i (range 100)m[mod]] [(m i 3) (m i 5) (m i 7) (m i 9)])
(for [i (range 100)m[#(mod i %)]] [(m 3) (m 5) (m 7) (m 9)])

;; default values on map gets can be very handy
(map #(if (= 0 (mod % 3)) "Fizz" %) (range 10))
(map #({0 "Fizz"} (mod % 3) %) (range 10))

 

I’ve posted these tricks plus some example code golf programs in a new github repository – clojure-golf

Advertisements
Comments
  1. Thomas says:

    FizzBuzz (kinda) in 77 characters:

    (map vector(range 1 99)(cycle[nil nil”fizz”])(cycle[nil nil nil nil”buzz”]))

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s