Skip to content

Instantly share code, notes, and snippets.

@piotr-yuxuan
Last active May 5, 2020 13:56
Show Gist options
  • Save piotr-yuxuan/efef180cf674b32c863bb459583c1f02 to your computer and use it in GitHub Desktop.
Save piotr-yuxuan/efef180cf674b32c863bb459583c1f02 to your computer and use it in GitHub Desktop.
FizzBuzz test

Assignment:

Write a program that prints the numbers from 1 to 100. But for multiples of three print "Fizz" instead of the number and for the multiples of five print "Buzz". For numbers which are multiples of both three and five print "FizzBuzz".

First, explicit solution. The one wich I immediately conceive when hearing at the problem.

(for [x (range 1 (inc 100))]
  (cond
    (and (zero? (mod x 3)) (zero? (mod x 5))) "FizzBuzz"
    (zero? (mod x 3)) "Fizz"
    (zero? (mod x 3)) "Buzz"
    :else x))

Then I present several progressive refinements. It's important for a code to be "given love". Given the context this code has to be inserted in, the preferred solution can change.

Name conditions to explain what you're dealing with

(for [x (range 1 (inc 100))]
  (let [[divisible-by-3? divisible-by-5?] (->> [3 5]
                                               (map #(mod x %))
                                               (map zero?))]
    (case [divisible-by-3? divisible-by-5?]
      [true true] "FizzBuzz"
      [true false] "Fizz"
      [false true] "Buzz"
      [false false] x)))

Name the results of the test, avoid meaningless boolean

(for [x (range 1 (inc 100))]
  (let [divisible-by-3? (if (-> 3 #(mod x %) zero?)
                          :divisible-by-3
                          :not-divisible-by-3)
        divisible-by-5? (if (-> 5 #(mod x %) zero?)
                          :divisible-by-5
                          :not-divisible-by-5)]
    (case [divisible-by-3? divisible-by-5?]
      [:divisible-by-3 :divisible-by-5] "FizzBuzz"
      [:divisible-by-3 :not-divisible-by-5] "Fizz"
      [:not-divisible-by-3 :divisible-by-5] "Buzz"
      [:not-divisible-by-3 :not-divisible-by-5] x)))

Name the code structure responsible for the condition

(defn- divisible-by?
  [number divisor]
  (let [division-rest (mod number divisor)
        divisible? (zero? division-rest)]
    (keyword (str (when-not divisible? "not-") "divisible-by-" divisor))))

(for [x (range 1 (inc 100))]
  (let [divisible-by (partial divisible-by? x)]
    (case [(divisible-by 3) (divisible-by 5)]
      [:divisible-by-3 :divisible-by-5] "FizzBuzz"
      [:divisible-by-3 :not-divisible-by-5] "Fizz"
      [:not-divisible-by-3 :divisible-by-5] "Buzz"
      [:not-divisible-by-3 :not-divisible-by-5] x)))

Some months later, reading this again…

I would definitely go for a more declarative way:

(let [responses {#(zero? (mod % 3)) "Fizz"
                 #(zero? (mod % 5)) "Buzz"}]
  (doseq [x (map inc (range 100))]
    (doseq [[pred response] responses]
      (when (pred x) (print response)))
    (print "\n")))

If advised to use transducers, I would use:

(let [responses {#(zero? (mod % 3)) "Fizz"
                 #(zero? (mod % 5)) "Buzz"}]
  (doall (eduction (map inc)
                   (map (fn [i]
                          (or (seq (mapcat (fn [[k v]]
                                             (when (k i)
                                               v))
                                           responses))
                              [i])))
                   (map #(apply str %))
                   (map println)
                   (range 100))))

Some years later, reading it again

We are lucky enough to use Clojure, a terse, concise language. Anyone with any knowledge of progamming most probably understands what mod is, so why going further?

(for [x (range 1 (inc 100))]
  (cond
    (and (zero? (mod x 3)) (zero? (mod x 5))) "FizzBuzz"
    (zero? (mod x 3)) "Fizz"
    (zero? (mod x 3)) "Buzz"
    :else x))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment