昨日まで約一週間、↓このサイトの 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)
これで余計な改行もなくなってきれいになった。