Skip to content

Instantly share code, notes, and snippets.

@kohyama
Last active August 29, 2015 13:58
Show Gist options
  • Save kohyama/10346936 to your computer and use it in GitHub Desktop.
Save kohyama/10346936 to your computer and use it in GitHub Desktop.
FibBuzz in Clojure
(defn fibs [a b] (cons a (lazy-seq (fibs b (+' a b)))))
(defn fizzbuzz [x]
(condp #(zero? (mod %2 %1)) x
15 "FizzBuzz"
5 "Buzz"
3 "Fizz"
x))
(def fibbuzz-seq (map fizzbuzz (fibs 1 1)))
; (take 100 fibbuzz-seq)
; -> (1 1 2 "Fizz" "Buzz" 8 13 "Fizz" ... 218922995834555169026N "FizzBuzz")
@kohyama
Copy link
Author

kohyama commented Apr 10, 2014

FibBuzz in Clojure

Google で "FibBuzz" を検索

フィボナッチ数列

(defn fibs [a b] (cons a (lazy-seq (fibs b (+' a b)))))

fibs は,

  • 2つの引数 a b をとり,
  • 二番目の引数 b と二つの引数の和 (+' a b) を引数として, 自分自身を呼び出した結果 (fibs ...)
  • を, 要素が必要とされた時になってから計算するよう遅延シーケンスにしたもの (lazy-seq ...)
  • の先頭に, 最初の引数を追加したシーケンス (cons a ...)

を返す関数です.

従って, 最初の 2 項を引数として fibs を呼び出すと, フィボナッチ数列を遅延シーケンスによって実現された無限数列として返します.

最初の 20 項は以下のようになります.

user=> (take 20 (fibs 1 1))
(1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765)

+ でなく +' を使っているのは, 値が大きくなったときに自動で多倍長整数に拡張するためです.

100 項まで取得します.

user=> (take 100 (fibs 1 1))
(1 1 2 3 5 8 ... 7540113804746346429 12200160415121876738N ... 354224848179261915075N)

+' を使っているため, 整数のデフォルト型である long がオーバーフローする際は, 多倍長整数 BigInteger に自動で型変換されます.
N が末尾についている整数は, 多倍長整数のリテラル表現です.

FizzBuzz

(defn fizzbuzz [x]
  (condp #(zero? (mod %2 %1)) x
    15 "FizzBuzz"
     5 "Buzz"
     3 "Fizz"
       x))

fizzbuzz は, 引数 x

  • 15 で割り切れれば "FizzBuzz" を,
  • 5 で割り切れれば "Buzz" を,
  • 3 で割り切れれば "Fizz" を,
  • いずれでもなければ x

返します.

condp は,

(condp p x
  t0 r0
  t1 r1
  ...
  rn)

のような形式で使い, p に真偽値 (として解釈できる値) を返す二引数関数を与えると

  • (p t0 x) が真ならば r0
  • (p t1 x) が真ならば r1
  • ...
  • いずれでもなければ rn

返します.

つまり

(condp #(zero? (mod %2 %1)) x
  15 "FizzBuzz"
   5 "Buzz"
   3 "Fizz"
     x)

は,

  • (zero? (mod x 15)) が真ならば "FizzBuzz" を,
  • (zero? (mod x 5)) が真ならば "Buzz" を,
  • (zero? (mod x 3)) が真ならば "Fizz" を,
  • いずれでもなければ x

返します.

(cond
  (zero? (mod x 15)) "FizzBuzz"
  (zero? (mod x 5))  "Buzz"
  (zero? (mod x 3))  "Fizz"
  :else x)

と同等です.

user=> (map fizzbuzz [10 11 12 13 14 15])
("Buzz" 11 "Fizz" 13 14 "FizzBuzz")
user=> (map fizzbuzz (range 1 101))
(1 2 "Fizz" 4 "Buzz" "Fizz" 7 8 "Fizz" "Buzz" 11 "Fizz" 13 14 "FizzBuzz" ... 97 98 "Fizz" "Buzz")
user=> (take 100 (map fizzbuzz (iterate inc 1)))
(1 2 "Fizz" 4 "Buzz" "Fizz" 7 8 "Fizz" "Buzz" 11 "Fizz" 13 14 "FizzBuzz" ... 97 98 "Fizz" "Buzz")

FibBuzz 列

(def fibbuzz-seq (map fizzbuzz (fibs 1 1)))

fibbuzz-seq は,

  • fibs に最初の二項 1 1 を与えて作ったフィボナッチ数列 (fibs 1 1)
  • の, 各項に fizzbuzz を適用 (map fizzbuzz ...)

したシーケンスです.

無限シーケンスですので, 有限項を取り出して利用します.

user=> (take 100 fibbuzz-seq)
(1 1 2 "Fizz" "Buzz" 8 13 "Fizz" ... 218922995834555169026N "FizzBuzz")

Clojure 演習

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