昨日まで約一週間、↓このサイトの GETTING STARTED をやってきた。
cf. Elixir
1 ~ 20 までのちょうど半分、10 までやったので、ここでちょっと気分を変えて、自分でスクリプトを書いてみよう。お題は定番の FizzBuzz 問題だ。
まず最初に書いたのがこれ:
defmodule FizzBuzz do def fizzbuzz(n) when rem(n, 15) == 0 do "FizzBuzz" end def fizzbuzz(n) when rem(n, 3) == 0 do "Fizz" end def fizzbuzz(n) when rem(n, 5) == 0 do "Buzz" end def fizzbuzz(n) do to_string(n) end def fizzbuzz_iter(n) when n >= 100 do IO.puts(fizzbuzz(n)) end def fizzbuzz_iter(n) do IO.puts(fizzbuzz(n)) fizzbuzz_iter(n + 1) end end FizzBuzz.fizzbuzz_iter(1)
実行例は示さないけど、ちゃんと期待通りに動いた。動いたんだけど、なんというかコードがすごく冗長。関数定義が複数の句を持てるのはいいけど、これはちょっと醜いな。
で、以前 Haskell でやった剰余を使わない方法でやってみた。
defmodule FizzBuzz do def fb(x) do case x do {{_, "Fizz"}, "Buzz"} -> "FizzBuzz\n" {{_, "Fizz"}, ""} -> "Fizz\n" {{_, ""}, "Buzz"} -> "Buzz\n" {{m, ""}, ""} -> "#{to_string(m)}\n" end end def fizzbuzz() do fizz = Stream.cycle(["", "", "Fizz"]) buzz = Stream.cycle(["", "", "", "", "Buzz"]) 1..100 |> Enum.zip(fizz) |> Enum.zip(buzz) |> Enum.map(&fb/1) end end FizzBuzz.fizzbuzz() |> IO.puts
だいぶすっきりしたけど、ネストしたタプルがあんまりよくないな。あ、出力用の文字列にいちいち改行文字がついてるのは、こうしないと全部つながって出力されるから。リストをパイプで IO.puts
に渡すと、全要素を出力してから改行するらしい。そんなわけで1つ余計に改行が出力されることになったんだけど、それはまあ、いいことにする。
もう少し改良してみよう。case
でパターンマッチさせるなら、何もパイプを使わずに n
を 3
や 5
で割った余り(とn
)でパターンマッチすればいい。
defmodule FizzBuzz do def fizzbuzz(n) do case {rem(n, 3), rem(n, 5), n} do {0, 0, _} -> "FizzBuzz\n" {0, _, _} -> "Fizz\n" {_, 0, _} -> "Buzz\n" {_, _, m} -> "#{to_string(m)}\n" end end end 1..100 |> Enum.map(&FizzBuzz.fizzbuzz/1) |> IO.puts
良くなった。パターンマッチもきれいだし、いいんじゃないかな。
[追記]
Enum.each/2
を覚えた!
defmodule FizzBuzz do def fizzbuzz(n) do case {rem(n, 3), rem(n, 5), n} do {0, 0, _} -> "FizzBuzz" {0, _, _} -> "Fizz" {_, 0, _} -> "Buzz" {_, _, m} -> to_string(m) end end end 1..100 |> Enum.map(&FizzBuzz.fizzbuzz/1) |> Enum.each(&IO.puts/1)
これで余計な改行もなくなってきれいになった。