秋冬おすすめのコーディング規約

Lisp Advent Calendar 2014、2 日目はりひにーさんで クマクマナルド南国帝国ホテル店の決闘 でした。

3 日目はわたくし、りひにーです。
早くもネタが無くなってまいりました。

そこで、FizzBuzz 問題を例に Scheme ってこう書くといいのかーと目の前が明るくなったような気になれたことを書こうと思います。

リストを生成する

とりあえず、1 から 100 までのリストを作る必要がありそうです。 そーいえば、iota という関数があったよなーと試しに gosh などでこんな風に打ってみます:

(iota 100)
;; => (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99)

しかし評価後の値を見ると分かるように、iota0 から始まってしまいます。
これはまずいですね!

map+1 しようかなーという邪悪な考えが脳裏を掠めますが、scheme iota で検索すると、iota にはもっと引数があるらしく:

(iota 100 1)
;; => (1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100)

こんな風に第 2 引数に初期値 1 を入れることで無事 1 から 100 までの整数のリストを得ることが出来ました。

ヽ(゚∀゚)ノ ワー

Fizz っぽかったりとか Buzz っぽいあるいは FizzBuzz っぽいか確かめたりする

次は Fizz ったり Buzz ったり FizzBuzz したりしなければなりません。
Schemer はとりあえずリストの要素をあれしてこれすればあとはなんとかなるという考えの持ち主らしいです。

なので、Fizz かどうか確かめる関数 fizz?:

(define (fizz? n)
  (equal? (modulo n 3) 0))

や Buzz かどうか確かめる関数 buzz?:

(define (buzz? n)
  (equal? (modulo n 5) 0))

また、FizzBuzz かどうか確かめる関数 fizz-buzz? は上記の関数が両方とも真を返したら FizzBuzz なので、こんな風に書きます:

(define (fizz-buzz? n)
  (and (fizz? n)
       (buzz? n)))

(l ω l〃) equal?equal と間違えないでね!*1

数字を Fizz とか Buzz とか FizzBuzz とかと置き換えたりする

上記で定義した関数を使って map するときにあれしてこれしたいので、to-fizz-buzz を定義します:

(define (to-fizz-buzz n)
  (cond ((fizz-buzz? n) "FizzBuzz")
        ((fizz? n)      "Fizz")
        ((buzz? n)      "Buzz")
        (:else          (number->string n))))

map する

(map to-fizz-buzz (iota 100 1))

おしまい。

(print (map to-fizz-buzz (iota 100 1)))

とか:

(for-each print (map to-fizz-buzz (iota 100 1)))

して楽しみましょう。

参考

*1:間違った