Skip to content

Instantly share code, notes, and snippets.

@ericnormand
Last active November 27, 2020 14:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ericnormand/ef7ffcfb451e1a97a1ce54955e60c0c5 to your computer and use it in GitHub Desktop.
Save ericnormand/ef7ffcfb451e1a97a1ce54955e60c0c5 to your computer and use it in GitHub Desktop.

FizzBuzz, but with no ifs

FizzBuzz is a classic interview exercise meant to filter out people who can't program. It tests that you can write conditionals, loops, and a basic modulo check for divisibility. While it seems kind of insulting to ask someone to solve such a trivial problem, imagine if the interviewee couldn't do it. It's a quick way to check for basic skill before wasting an hour in the interview.

This week, you're going to write FizzBuzz, but just to show off to the interviewer, you're not going to write any conditionals or loops. Just to be clear, that means no if, when, cond, or case expressions.

Here are the rules:

Write a function that is passed a sequence of integers.

For each integer, if it's divisible by 3, print "Fizz". If it's divisible by 5, print "Buzz". If it's divisible by both 3 and 5, print "FizzBuzz". Finally, if it's divisible by neither, print the number itself. Each printing should be on a separate line.

Email submissions to eric@purelyfunctional.tv before September 13, 2020. You can discuss the submissions in the comments below.

(ns tst.demo.core
(:use tupelo.core tupelo.test))
(defn div-3? [n] (zero? (rem n 3))) ; hidden `if` statement
(defn div-5? [n] (zero? (rem n 5))) ; hidden `if` statement
(defn fizzbuzz
[n]
(let [factors [(div-3? n) (div-5? n)]
result (get {[true false] "Fizz"
[false true] "Buzz"
[true true] "FizzBuzz"} factors
n)] ; default has hidden `if`
result))
(dotest
(is= [true false false true false false true]
(mapv div-3? (thru -3 3)))
(is= [true false false false false true false false false false true]
(mapv div-5? (thru -5 5)))
(is= ["FizzBuzz" -14 -13 "Fizz" -11 "Buzz" "Fizz" -8 -7 "Fizz" "Buzz" -4 "Fizz" -2 -1
"FizzBuzz" 1 2 "Fizz" 4 "Buzz" "Fizz" 7 8 "Fizz" "Buzz" 11 "Fizz" 13 14
"FizzBuzz"]
(mapv fizzbuzz (thru -15 15))))
(defn fizzbuzz [n]
(let [fizzes (cycle ["" "" "Fizz"])
buzzes (cycle ["" "" "" "" "Buzz"])
pattern (map str fizzes buzzes)
numbers (map str (rest (range)))]
(take n (map #(some not-empty %&) pattern numbers))))
(doseq [x (fizzbuzz 100)]
(println x))
(defn fizzbuzz [xs]
(let [x (first xs)]
(and x (let [div-3 (= (mod x 3) 0)
div-5 (= (mod x 5) 0)]
(and div-3 (print "Fizz"))
(and div-5 (print "Buzz"))
(and (not div-3) (not div-5) (print x))
(println)
(recur (rest xs))))))
(defn fizzbuzz [ns]
(doseq [n ns]
(prn (get {[0 nil] "Fizz" [nil 0] "Buzz" [0 0] "FizzBuzz"}
[(get {0 0} (rem n 3)) (get {0 0} (rem n 5))] n))))
(ns clojure-challenge.issue-394)
(defn fizzbuzzer [x]
(or
(and (= 0 (mod x 15)) "fizzbuzz")
(and (= 0 (mod x 5)) "buzz")
(and (= 0 (mod x 3)) "fizz")
(str x)))
(defn -main
"Main function"
[]
(println (->> (range 31)
(map fizzbuzzer))))
(defn fizzbuzz [n]
(letfn [(f [[div s]] (and (zero? (mod n div)) s))]
(or (some f [[15 "FizzBuzz"] [5 "Buzz"] [3 "Fizz"]]) n)))
(defn print-fizzbuzz [ns]
(dorun (map (comp println fizzbuzz) ns)))
;; First solution
(defn fizz-buzz-or-number
[x]
(
["FizzBuzz" x x "Fizz" x "Buzz" "Fizz" x x "Fizz" "Buzz" x "Fizz" x x]
(mod x 15)))
(defn fizz-buzz
[xs]
(doseq [x (map fizz-buzz-x xs)]
(println x)))
(comment
(fizz-buzz (range 1 101))
(fizz-buzz [1 3 5 10 12 13 15])
)
;; Alternative solution.
;; I like this better, but it works better for a continuous range of numbers
(defn- make-seq
"Makes a sequence that returns name at each nth element and nil for others"
[name nth]
(cycle (concat (repeat (dec nth) nil) [name])))
(def fizzes (make-seq "Fizz" 3))
(def buzzes (make-seq "Buzz" 5))
(def fizzbuzzes (make-seq "FizzBuzz" 15))
(def first-non-nil #(some identity %&))
;; a lazy seq of FizzBuzzes, Fizzes, Buzzes, and numbers
(def fizz-buzz-seq
(map first-non-nil fizzbuzzes fizzes buzzes (iterate inc 1)))
(defn fizz-buzz-range
"Returns FizzBuzz elements from start (inclusive) to end
(exclusive), where start defaults to 1."
([end] (fizz-buzz-range 1 end))
([start end]
{:pre [(< start end)]}
(->> fizz-buzz-seq
(drop (dec start))
(take (- end start)))
))
;; The fizz-buzz-range works well for continuous ranges as the original FizzBuzz problem.
;; This function is to satisfy the spec in the newsletter (ie. pass in integers that could
;; be non-continuous)
(defn fizz-buzz-alternative
[xs]
(doseq [x (map #(first (fizz-buzz-range % (inc %))) xs)]
(println x)))
(comment
(fizz-buzz-range 101)
(fizz-buzz-range 100 201)
(fizz-buzz-alternative [1 3 5 9 10 12 13 15])
)
(ns purely-functional-tv-challenges.n394-fizz-buzz
(:require [clojure.string :as str]))
(defn fizz-buzz-infinite-seq
"Infinite fizz buzz sequence where each element is either
\"Fizz\",
\"Buzz\",
\"FizzBuzz\",
or
a string representing a natural number"
[]
(let [fizzes (cycle ["" "" "Fizz"])
buzzes (cycle ["" "" "" "" "Buzz"])
fizzes-and-buzzes (map str fizzes buzzes)
ints-as-strings (->> (range) (map inc) (map str))]
(->> (map vector fizzes-and-buzzes ints-as-strings) ; Seq of pairs of strings
; The line below is where the conditional sneaks in, because we check
; whether the element is a fizz/buzz/fizzbuzz or just a "normal" number.
; If it's a "normal" number then the fizzes-and-buzzes element will be blank.
(map #(drop-while str/blank? %) ,,,) ;
(map first ,,,))))
(defn fizz-buzz
"For each x in xs, print Fizz, Buzz, FizzBuzz, or the number itself, each
on a new line.
Assume all xs are 1 or greater."
[xs]
(let [fizz-buzz-seq (fizz-buzz-infinite-seq)]
(doseq [x xs]
(println (nth fizz-buzz-seq (dec x))))))
@KingCode
Copy link

KingCode commented Sep 7, 2020

Replacing doseq with map:

(defn fizzbuzz [xs]
  (let [fizzers (cycle [nil nil "Fizz"]) 
        buzzers (cycle [nil nil nil nil "Buzz"])
        fizzbuzzers (cons nil (map #(seq (str % %2)) fizzers buzzers))]    
    (->> xs 
         (map (fn [x]
                (->> (or (nth fizzbuzzers (max x (- x)))
                         [x])
                     ((fn [s] (->> s (apply str) (str "\n")))))))
         (apply println))))

@daveschoutens
Copy link

The instructions say 'loops and conditionals' are off-limits. Can you clarify what counts as a 'loop' for the purposes of this exercise? It seems like for might be out, but what about map, or other seq functions? (Or indeed, doseq)?

@e0d
Copy link

e0d commented Sep 7, 2020

(defn fizzbuzz [nums]
  (let [fizz ["Fizz"]
        buzz ["Buzz"]]
    (doseq [num nums]
      (let [out (str (str (get-in fizz [(mod num 3)])) (str (get-in buzz [(mod num 5)])))]
        (and (seq out) (println out))
        (or (seq out) (println num))))))

@jeremyvdw
Copy link

(defn fizzbuzz [xs]
  (let [fizz #(zero? (mod % 3))
        buzz #(zero? (mod % 5))
        fizz-buzz (every-pred fizz buzz)]
    (reduce
      (fn [res el]
        (println (or (and (fizz-buzz el) "FizzBuzz")
                     (and (buzz el) "Buzz")
                     (and (fizz el) "Fizz")
                     el))
        res)
      nil
      xs)))

@KingCode
Copy link

KingCode commented Sep 7, 2020

That’s an interesting question. We could probably say that using or or and is a direct use of conditionals since they output ifs. I chose to avoid relying on decision branching in the code I write as per the instructions: so the ones you mention are certainly ok!

The instructions say 'loops and conditionals' are off-limits. Can you clarify what counts as a 'loop' for the purposes of this exercise? It seems like for might be out, but what about map, or other seq functions? (Or indeed, doseq)?

@ninjure
Copy link

ninjure commented Sep 8, 2020

(defn fizzbuzz [n]
  (let [m {[false false] n
           [true  false] "fizz"
           [false true ] "buzz"
           [true  true ] "fizzbuzz"}]
    (m [(zero? (mod n 3))
        (zero? (mod n 5))])))

@ninjure
Copy link

ninjure commented Sep 8, 2020

(defn fizzbuzz [n]
  (some identity
        [(get ["fizzbuzz"] (mod n 15))
         (get ["buzz"] (mod n 5))
         (get ["fizz"] (mod n 3))
         n]))

@daveschoutens
Copy link

daveschoutens commented Sep 8, 2020

(defn fizzbuzz-val [n]
  (get {0 (get {0 "FizzBuzz"} (mod n 15) "Fizz")} ;; Does the 'default' argument of `get` count as cheating? :-)
       (mod n 3) 
       (get {0 "Buzz"} (mod n 5) n)))

(defn fizzbuzz [xs]
  (doseq [x xs] (println (fizzbuzz-val x))))

Or, if doseq is off-limits:

;; same `fizzbuzz-val as above

(defn do-fizzbuzz
  ([x] (println (fizzbuzz-val x)))
  ([x & xs]
    (do-fizzbuzz x)
    (apply do-fizzbuzz xs)))

(defn fizzbuzz [xs]
  (apply do-fizzbuzz xs))

@mchampine
Copy link

mchampine commented Sep 8, 2020

Or, if doseq is off-limits:

If doseq is too "loopy" then I would think recursion is too! :)

@werand
Copy link

werand commented Sep 8, 2020

(defn fizzbuzz [s]
  (map (fn [n]
         (get ["fizzbuzz"] (mod n 15)
              (get ["fizz"] (mod n 3)
                   (get ["buzz"] (mod n 5)
                        n))))
       s))

@ericnormand
Copy link
Author

The instructions say 'loops and conditionals' are off-limits. Can you clarify what counts as a 'loop' for the purposes of this exercise? It seems like for might be out, but what about map, or other seq functions? (Or indeed, doseq)?

Sorry to get to this so late! For me, personally, doseq is a loop, but map is not. As for run!, I'm not sure! I wanted to leave it a little ambiguous to see what people would come up with.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment