Clojure で、いろいろな FizzBuzz

2022-02-25

数値を一つ渡すと、FizzBuzzの結果を返す関数をいくつか作ってみました。

(fizz-buzz 1) ;=> 1
(fizz-buzz 3) ;=> "fizz"
(fizz-buzz 5) ;=> "buzz"
(fizz-buzz 15) ;=> "fizz buzz"

cond

(defn fizz-buzz [n]
  (cond
    (zero? (mod n 15)) "fizz buzz"
    (zero? (mod n 3)) "fizz"
    (zero? (mod n 5)) "buzz"
    :else n))

「(zero? (mod ))」の重複が気になります。

condp

最初に↓を思いついたけど、「(zero? (mod )) 」の重複は残っているし、文字数も増えています。けど個人的には、問題がそのままコードになっているようで読みやすいかも。

(defn fizz-buzz [n]
  (condp =  [(zero? (mod n 3))
             (zero? (mod n 5))]
    [true true] "fizz buzz"
    [true false] "fizz"
    [false true] "buzz"
    [false false] n))

でも、condpを使わなくても似たように作れるわけで。(Clojure のマップは関数として動作し、引数に一致するキーの値を返す)

(defn fizz-buzz [n]
  ({[true true] "fizz buzz" ;マップを作成
    [true false] "fizz"
    [false true] "buzz"
    [false false] n} 
   [(zero? (mod n 3)) ;検索するキーを作成
    (zero? (mod n 5))]))

calvaのチュートリアルで見つけた、シンプルな condp。condp の fn に渡る引数の順番に注意。少しハマりました。

(defn fizz-buzz [n]
  (condp (fn [a b] (zero? (mod  b a))) n
    15 "fizz buzz" ;n を15で割ったあまりが0なら"fizz buzz"を返す
    3 "fizz"
    5 "buzz"
    n))

defmulti

同じく、calvaのチュートリアルで見た defmulti を使う例

(defmulti fizz-buzz (juxt #(zero? (mod % 3))
                          #(zero? (mod % 5))))
(defmethod fizz-buzz [true true] [n] "fizz buzz")
(defmethod fizz-buzz [true false] [n] "fizz")
(defmethod fizz-buzz [false true] [n] "buzz")
(defmethod fizz-buzz [false false] [n] n)

条件分岐なし

Yehonathan Sharvit のブログ で見つけた,if,cond,condpなどを使わない例。

FizzBuzzの答えを先に作って、指定された数値の答えを取ってきて返す。

(defn choice [a b]
  (clojure.string/join (or (seq a) (seq b))))
;(choice "" "1") => "1"
;(choice "Fizz" "3") => "Fizz"

(defn fizzbuzz [n]
  (let [fizzes (cycle ["" "" "Fizz"])
        buzzes (cycle ["" "" "" "" "Buzz"])
        words (map str fizzes buzzes)
        numbers (map str (rest (range)))]
    (take n (map choise words numbers)))) ;ClojureScriptの場合は、choice を max で代用可能。