ElixirでFizzBuzz錬金術

プログラミングは文学である。1つのアルゴリズムにいろんな表現の仕方があり、そこに芸術性を見出すことが出来る。

しかし芸術とはゴミを作ることでもあるのだ。

# なにこれ

アルケミストの遊びです。ここでは敢えていろんなゴミを錬金してみましょう。

基本的に真似しちゃダメですが、「こんな書き方もできるのか」とか思って調べてみるきっかけになればと思います。

# 1. `if` の濫用

```elixir
defmodule FizzBuzz do
  def say(n) do
    if Integer.mod(n, 15) == 0 do
      "FizzBuzz"
    else
      if Integer.mod(n, 3) == 0 do
        "Fizz"
      else
        if Integer.mod(n, 5) == 0 do
          "Buzz"
        else
          n
        end
      end
    end
  end
end

for n <- 1..30 do
  IO.puts FizzBuzz.say(n)
end
```

Elixir で条件分岐には `if` はあんまり使いません。Non-alchemist が Elixir 書くと稀によくこうなる。

# 2. `cond` を使う

```elixir
defmodule FizzBuzz do
  def say(n) do
    cond do
      Integer.mod(n, 15) == 0 -> "FizzBuzz"
      Integer.mod(n, 3)  == 0 -> "Fizz"
      Integer.mod(n, 5)  == 0 -> "Buzz"
      :else                   -> n
    end
  end
end

for n <- 1..30 do
  IO.puts FizzBuzz.say(n)
end
```

まあギリギリ及第点いくかいかないかくらいですかね。 `:else` がチャームポイント。

# 3. `case` で guard に `rem` を使う

```elixir
defmodule FizzBuzz do
  def say(n) do
    case n do
      n when rem(n, 15) == 0 -> "FizzBuzz"
      n when rem(n, 3)  == 0 -> "Fizz"
      n when rem(n, 5)  == 0 -> "Buzz"
      n                      -> n
    end
  end
end

for n <- 1..30 do
  IO.puts FizzBuzz.say(n)
end
```

結構Elixirっぽくなってきました。ですがこれは冗長です。

(今回 `n` は `pos_integer` なので、`Integer.mod/2` と `rem/2` の違いは問題になりません)

# 4. 複数句による関数定義でパターンマッチ

```elixir
defmodule FizzBuzz do
  def say(n) when rem(n, 15) == 0, do: "FizzBuzz"
  def say(n) when rem(n, 3)  == 0, do: "Fizz"
  def say(n) when rem(n, 5)  == 0, do: "Buzz"
  def say(n), do: n
end

for n <- 1..30 do
  IO.puts FizzBuzz.say(n)
end
```

最後の `def` をalignすべきかどうかが迷いどころ。

# 5. `for` ではなく `Enum.each/2`

```elixir
defmodule FizzBuzz do
  def say(n) when rem(n, 15) == 0, do: "FizzBuzz"
  def say(n) when rem(n, 3)  == 0, do: "Fizz"
  def say(n) when rem(n, 5)  == 0, do: "Buzz"
  def say(n), do: n
end

Enum.each(1..30, fn n ->
  IO.puts FizzBuzz.say(n)
end)
```

別に30個の `:ok` を並べたいわけではないので `Enum.each/2` を使います。

# 小休止

ここまで来てやっとElixirとしてまともなコードになったような気がします。

此処からが本番ですよ。

# 6. `defmodule` しない

```elixir
fizz_buzz = fn
  n when rem(n, 15) == 0 -> "FizzBuzz"
  n when rem(n, 3)  == 0 -> "Fizz"
  n when rem(n, 5)  == 0 -> "Buzz"
  n                      -> n
end

Enum.each(1..30, fn n ->
  IO.puts fizz_buzz.(n)
end)
```

`fn` の引数部でもパターンマッチが出来て、複数の枝を扱うことが出来ます。

`fn` で作った関数を呼ぶときは `fizz_buzz.(n)` というふうに `.` が必要になります。

# 7. 複数句による関数定義でたらい回す

```elixir
defmodule FizzBuzz do
  def say(n), do: say(n, rem(n, 3))

  def say(n, 0), do: say(n, "Fizz", rem(n, 5))
  def say(n, _), do: say(n, "",     rem(n, 5))

  def say(_, fizz, 0), do: fizz <> "Buzz"
  def say(n, "",   _), do: n
  def say(_, fizz, _), do: fizz
end

Enum.each(1..30, fn n ->
  IO.puts FizzBuzz.say(n)
end)
```

なにも全部同じ名前にしなくても…とは思いますが、きっと私なりの美学がそこにあるのでしょう。

# 8. `Stream.unfold/2`を使う

```elixir
Stream.unfold({1, 1, 1}, fn
  {n, 3, 5} -> {"FizzBuzz", {n + 1, 1,     1    }}
  {n, 3, b} -> {"Fizz",     {n + 1, 1,     b + 1}}
  {n, f, 5} -> {"Buzz",     {n + 1, f + 1, 1    }}
  {n, f, b} -> {n,          {n + 1, f + 1, b + 1}}
end) \
|> Stream.take(30) \
|> Enum.each(&IO.puts/1)
```

(そのままiexにコピペして実行できるように ` \` を入れてます)

ガラッとアプローチを変えて、FizzBuzzストリームを作るという方針にしてます。`{n, f, b}` がaccumulatorになっていて、3つとも1ずつ増やして`f`が`3`になったら`"Fizz"`、`b`が`5`になったら`"Buzz"`、という具合です。

`Enum.take/2` ではなく `Stream.take/2` を使ってるのもポイント。

# 9. Eitherっぽいものを作って香ばしく

```elixir
# Not Either but chemistrical
defmodule Ether do
  defstruct [
    which: :left,
    value: nil,
  ]

  @type t :: %__MODULE__{
    which: :right | :left,
    value: any,
  }

  def right(val), do: %__MODULE__{which: :right, value: val}
  def left(val),  do: %__MODULE__{which: :left,  value: val}

  def lift2(
    fun,
    %__MODULE__{which: :right, value: v1},
    %__MODULE__{which: :right, value: v2}
  ), do: %__MODULE__{which: :right, value: fun.(v1, v2)}
  def lift2(
    _,
    %__MODULE__{which: :right, value: v1},
    %__MODULE__{which: :left}
  ), do: %__MODULE__{which: :left, value: v1}
  def lift2(
    _,
    %__MODULE__{which: :left},
    %__MODULE__{which: :right, value: v2}
  ), do: %__MODULE__{which: :left, value: v2}
  def lift2(
    _,
    %__MODULE__{which: :left, value: v1},
    %__MODULE__{which: :left}
  ), do: %__MODULE__{which: :left, value: v1}
end

defimpl String.Chars, for: Ether do
  def to_string(%Ether{value: val}), do: Kernel.to_string(val)
end

defmodule FizzBuzz do
  def say(n) do
    Ether.lift2(&<>/2, fizz(n), buzz(n))
  end

  defp fizz(n) when rem(n, 3) == 0, do: Ether.right("Fizz")
  defp fizz(n), do: Ether.left(n)

  defp buzz(n) when rem(n, 5) == 0, do: Ether.right("Buzz")
  defp buzz(n), do: Ether.left(n)
end

Enum.each(1..30, fn n ->
  IO.puts FizzBuzz.say(n)
end)
```

`lift2` が本当に `lift2` になってるかはちょっと怪しいです。どこで改行するかも悩みどころ。

`IO.puts/1` に直接渡すために `String.Chars` プロトコルを実装しています。そのために `defstruct`。

前半はただの準備なので、`defmodule FizzBuzz do` からが実際のコードと思うと結構きれいに見えます(断定)。

# 10. (それっぽいタイトル)

```elixir
# あなたの錬金したゴミ、あるいはアート



```