Fizzing and Buzzing With Clojure

I’ve known about the FizzBuzz problem for a few years. I’ve written solutions for it in a few languages, but never posted them. I’ve been working with Clojure lately, and after reading articles about how many job applicants can’t solve a simple problem like this here, here and here, I decided to do a Clojure version. (It baffles me that someone who claims to be a developer can’t come up with a solution for this, no matter how good or bad it might be.)

I ended up doing it three different ways. The first is a simple first-cut solution. The second is somewhat better, I think, and the third is a refinement of the second. In all three cases, they use a nested function to do the evaluation, and return a lazy, infinite sequence. Here’s the first

[clojure]
(defn fizzbuzz []
(map (fn [i] (cond
(and (= (rem i 3) 0)
(= (rem i 5) 0)) "FizzBuzz!"
(= (rem i 3) 0) "Fizz!"
(= (rem i 5) 0) "Buzz!"
:else i))
(iterate inc 1)))

(doseq [i (take 100 (fizzbuzz))]
(println i))
[/clojure]

This solution does work, but I have a problem with the fact that the division tests are done twice. I think doing those tests twice increases the chances of making a mistake. The second version does the tests one time, assigning the results to locals. It then checks them for nil, and concatenates them together, relying on the fact that a nil will not print.

[clojure]
(defn fb []
(let [fb1 (fn [n]
(let [fizz (if (= (rem n 3) 0) "Fizz")
buzz (if (= (rem n 5) 0) "Buzz")]
(if (or fizz buzz)
(str fizz buzz "!")
n)))]
(map fb1 (iterate inc 1))))

(doseq [i (take 100 (fb))]
(println i))
[/clojure]

In this version, instead of passing an anonymous function to map, I assigned it to a local in a let expression. You can see that I only do the math once, assigning locals with either the appropriate word, or nil. I then check that one or the other of the locals are non-nil, cat them together and return it. If both are nil, the number itself is returned.

The third version is almost identical to the second. The only difference is that the second one used a let expression, and the third one uses a letfn expression. It’s effectively the same thing, but the third one is ever-so-slightly shorter, and I think every-so-slightly easier to read.

[clojure]
(defn fb2 []
(letfn [(fb3 [n]
(let [fizz (if (= (rem n 3) 0) "Fizz")
buzz (if (= (rem n 5) 0) "Buzz")]
(if (or fizz buzz)
(str fizz buzz "!")
n)))]
(map fb3 (iterate inc 1))))

(doseq [i (take 100 (fb2))]
(println i))
[/clojure]

I don’t claim that these are particularly good solutions, though I do claim they work correctly. Any Clojure experts care to point out problems and/or offer suggestions?

This entry was posted in tech and tagged , , . Bookmark the permalink.

3 Responses to Fizzing and Buzzing With Clojure

  1. Bob Foster says:

    Complete clojure novice, but how about something with loop and recur?

  2. Bob Foster says:

    I don’t claim this is correct – I neither read nor write clojure – or even that I balanced the parens correctly, but something like:

    [clojure]
    (loop [i 1]
    (let [fizz (if (= (rem n 3) 0) "Fizz")
           buzz (if (= (rem n 5) 0) "Buzz")]
               (if (or fizz buzz)
                   (println (str fizz buzz "!"))
                   (println n)))
    (if (<= i 100)
    (recur (inc i))))
    [/clojure]

  3. Joey Gibson says:

    You mixed i and n, and your loop went to 101, but other than that, it works just fine. Here’s the corrected version:

    [clojure]
    (loop [n 1]
    (let [fizz (if (= (rem n 3) 0) "Fizz")
    buzz (if (= (rem n 5) 0) "Buzz")]
    (if (or fizz buzz)
    (println (str fizz buzz "!"))
    (println n)))
    (if (< n 100)
    (recur (inc n))))
    [/clojure]

Comments are closed.