The FizzBuzz Kata in Clojure

My first Clojure code was the Bowling Kata I wrote about in my last post. Today I did the FizzBuzz Kata and the stage 2 requirements. This was a lot simpler than the Bowling Kata.

There is not much to say about it, here is my test code:

(ns fizzbuzztest
 (:use fizzbuzz clojure.test))

(deftest nonefizzbuzz-numbers-return-itself
 (is (= (fizzbuzz '(1 2 4 7)) '(1 2 4 7))))

(deftest multiple-of-three-and-not-five-return-fizz
 (is (= (fizzbuzz '(3 6 9 12 18 21 24 27)) (take 8 (repeat "fizz")) )))

(deftest multiple-of-five-and-not-three-return-buzz
 (is (= (fizzbuzz '(5 10 20 25 40 50 55 65)) (take 8 (repeat "buzz")) )))

(deftest multiple-of-three-and-five-return-fizzbuzz
 (is (= (fizzbuzz '(15 30 45 60 75 90 105 120)) (take 8 (repeat "fizzbuzz")) )))

(deftest three-in-number-returns-fizz
 (is (= (fizzbuzz '(3 13 23 31 103)) (repeat 5 "fizz"))))

(deftest five-in-number-returns-buzz
 (is (= (fizzbuzz '(5 52 58 151 502)) (repeat 5 "buzz"))))

(run-tests)

and here is the implementation:

(ns fizzbuzz)

(defn- numeric-fizz? [n]
 (= (rem n 3) 0))

(defn- string-fizz? [n]
 (some #(= % \3) (str n)))

(defn- fizz? [n]
 (or (numeric-fizz? n) (string-fizz? n)))

(defn- numeric-buzz? [n]
 (= (rem n 5) 0))

(defn- string-buzz? [n]
 (some #(= % \5) (str n)))

(defn- buzz? [n]
 (or (numeric-buzz? n) (string-buzz? n)))

(defn- fizzbuzz? [n]
 (and (fizz? n) (buzz? n)))

(defn- replace-fizz-buzz [n]
 (cond
 (fizzbuzz? n) "fizzbuzz"
 (fizz? n) "fizz"
 (buzz? n) "buzz"
 (number? n) n))

(defn fizzbuzz [numbers]

 (map replace-fizz-buzz numbers))

I like the function that checks if a number contains a ‘3’ character. Although it looks weird it is not so hard to read after  getting a little used to Clojure code.

(defn- string-fizz? [n]
 (some #(= % \3) (str n)))

This functions gets a number n as parameter, which is converted to a string with (str n). The some calls the anonymous function #(= % \3) on each element (i.e. character) of n as string. If the anonymous function returns true, some return true as well. The anonymous function simply checks if its parameter (the %) is the character 3.

There is one issue with the cond in replace-fizz-buzz:the last condition (number? n) is just a dummy condition. Actually I don’t want it. If none of the other conditions match, it should just return the n. But that is not possible with cond. I’m looking for a better solution…

I also created my first Leiningen build script to run the tests. Leiningen is a build tool for Clojure. Here is the build script (named project.clj and placed in the project root folder):


(defproject fizzbuzz "1.0.0"
 :description "A cloure implmementation of the FizzBuzz kata."
 :dependencies [[org.clojure/clojure "1.1.0"]])

Quite simple ;-)

Running the following command:

$ lein test fizzbuzztest

prints:

 [null] Testing fizzbuzztest
 [null] Ran 6 tests containing 6 assertions.
 [null] 0 failures, 0 errors.
 [null] --------------------
 [null] Total:
 [null] Ran 6 tests containing 6 assertions.
 [null] 0 failures, 0 errors.

Works, but I have no idea, why it prints the [null] stuff.

The Bowling Kata in Clojure

I have bee reading Programming Clojure lately to learn about functional programming. Interest in functional programming is increasing so it might be a good idea to get familiar with it. :-)

Clojure is a Lisp like programming language that runs on the Java VM and also on the .NET CLR.

I didn’t have much knowledge about functional programming before and the book still left me with a lot of questions. After practicing OOP for most of my programming life, Clojure is very strange at first.

To get a little bit practice at it I spend a couple of hours (reading documentation, playing with the REPL) tdd’ing the Bowling Kata in Clojure.

There are already a few versions of the Clojure Bowling Kata on the net: Uncle Bob, Micah, Halloway. There is a lot of info in the comments of the first article and the third link points to a version from the author of the Clojure book mentioned above.

Here is mine :-)

I didn’t read the links in detail before I started because the idea was to do this on my own. So I guess there is still some potential for improvements.

If you are looking for an environment, you may try Intellij IDEA’s Community Edition and their Clojure plugin La-Clojure. That’s what I have used.

Unfortunately WordPress’s [sourcode] tag doesn’t seem to know Clojure yet. So no highlighting.

The tests:

(ns test
  (:use
    bowling
    clojure.contrib.test-is))

(defn- zero
  ([] (repeat 2 0))
  ([n] (repeat (* n 2) 0)))

(defn- spare
  ([] (repeat 2 5))
  ([n] (repeat (* n 2) 5)))

(deftest the-score-of-a-gutter-game-is-zero
  (is (= 0 (score (zero 10)))))

(deftest the-score-of-one-rolls-only-is-twenty
  (is (= 20 (score (repeat 20 1)))))

(deftest the-score-of-a-spare-includes-the-next-roll
  (is (= 16 (score (concat (spare) '(3 0)  (zero 8)))))
  (is (= 16 (score (concat (zero)   (spare) '(3 0) (zero 7)))))
  (is (= 16 (score (concat (zero 2) (spare) '(3 0) (zero 6)))))
  )

(deftest two-spares-in-a-row
  (is (= 31 (score (concat (spare 2) '(3 0) (zero 7))))))

(deftest three-spares-in-a-row
  (is (= 46 (score (concat (spare 3) '(3 0) (zero 6))))))

(deftest the-score-of-a-strike-includes-the-next-two-rolls
  (is (= 24 (score (concat '(10) '(3 4) (zero 8))))))

(deftest spare-in-last-frame
  (is (= 16 (score (concat (zero 9) (spare) '(3))))))

(deftest strike-in-last-frame
  (is (= 17 (score (concat (zero 9) '(10) '(3 4))))))

(deftest the-score-of-a-perfect-game-is-300
  (is (= 300 (score (repeat 12 10)))))

(run-tests)

and the code:

(ns bowling)

(defn- sum [rolls]
  (reduce + rolls))

(defn- spare? [rolls]
  (= 10 (sum (take 2 rolls))))

(defn- strike? [rolls]
  (= 10 (first rolls)))

(defn- more? [rolls]
  (seq rolls))

(defn- score-strike [rolls]
  (sum (take 3 rolls)))

(defn- score-spare [rolls]
  (sum (take 3 rolls)))

(defn- score-frame [rolls]
  (sum (take 2 rolls)))

(def score)

(defn- score-after-strike [rolls]
  (score
    (if (= 3 (count rolls))
      (drop 3 rolls)
      (drop 1 rolls)
    )))

(defn- score-after-frame [rolls]
  (score (drop 2 rolls)))

(defn score
  "calculate the bowling score of a [rolls] sequence"
  [rolls]
  (cond
    (empty? rolls)     0
    (strike? rolls)    (+ (score-strike rolls) (score-after-strike rolls))
    (spare?  rolls)    (+ (score-spare rolls)  (score-after-frame rolls))
    (more?   rolls)    (+ (score-frame rolls)  (score-after-frame rolls))
    )
  )

A few notes:

The last test I added (the-score-of-a-perfect-game-is-300) made me insert that strange if to (score-after-strike). This didn’t happen on the Java version. Because there is no loop that counts the frames in the Clojure version, one has to detect the last frame some other way. Without it, the last two strikes would not only be counted as the bonus for the 10th strike but also as an 11th and 12th frame.

I think my solution is not too bad for my first piece of Clojure code.

What I dislike is that every call in the (score) function has the rolls sequence as parameter. That is a lot of duplication noise. I see an object here ;-)

In the test I’m not sure if the (spare) and (zero) helper methods improve the readability. Maybe simply writing out every single roll would be easier to understand.

What do you think of my Clojure Bowling Kata? Is there anything that’s totally not the Clojure way? ;)

Update (10.2.2010)

After some feedback on the clojure group, I fixed the forward declaration of the (score) function. It should use (declare) and instead of (def) and I modified the score function to use (condp) with (apply). That is a small trick to avoid the rolls parameter on the predicate functions (empty? etc.). From a readability point, I think I still prefer the simple (cond) from my first version.

(ns bowling)

(defn- sum [rolls]
  (reduce + rolls))

(defn- spare? [rolls]
  (= 10 (sum (take 2 rolls))))

(defn- strike? [rolls]
  (= 10 (first rolls)))

(defn- more? [rolls]
  (seq rolls))

(defn- score-strike [rolls]
  (sum (take 3 rolls)))

(defn- score-spare [rolls]
  (sum (take 3 rolls)))

(defn- score-frame [rolls]
  (sum (take 2 rolls)))

(declare score)

(defn- score-after-strike [rolls]
  (score
    (if (= 3 (count rolls))
      (drop 3 rolls)
      (drop 1 rolls)
    )))

(defn- score-after-frame [rolls]
  (score (drop 2 rolls)))

(defn score
  "calculate the bowling score of a [rolls] sequence"
  [rolls]
  (condp apply [rolls]
    empty?   0
    strike?  (+ (score-strike rolls) (score-after-strike rolls))
    spare?   (+ (score-spare rolls)  (score-after-frame rolls))
    more?    (+ (score-frame rolls)  (score-after-frame rolls))
    )
  )