Elixirらしいif(xx){str += xxx}はこれだ（※個人の感想です）

Elixir歴１か月です

初心者の記事は今しか書けないとおもうので、怖いものなしに書いてみます。
先輩方のご意見など歓迎いたします。

Elixir練習用に、[minesweeper](https://github.com/masahiro-999/phx-minesweeper)を作ってみました。
この中のget_class_string()が力づくな関数なんです。

手続き型の言語の場合 `if(xx){str += xxx}`のように追加していけばいいですが、そうはいかない。
この例と類似した問題は、今後もあると思うので、これを例題に、Elixirらしい記述を検討してみます。

# 初期バージョン

```elixir:tutorial.ex
defmodule Tutorial002 do
  def get_class_string(board, index) do
    cell = elem(board.cells, index)
    (["cell"]
    ++
    (if board.clear, do: ["clear"], else: [])
    ++
    (if board.gameover, do: ["gameover"], else: [])
    ++
    (if (board.gameover or board.clear) and index in board.bomb, do: ["bomb"], else: [])
    ++
    if cell == :marked, do: ["flag"], else: (if cell != :nomark, do: ["open"], else: []))
    |> Enum.join(" ")
  end
end
```

# リファクタリング第一弾

条件にあった場合には、文字を追加。reduceでaccに文字を足しこんでいってみます。
reduceを使えばElixirらしくなるかも。

```elixir:tutorial1.ex
  def get_class_string(board, index) do
    cell = elem(board.cells, index)
    [
      {board.clear, "clear"},
      {board.gameover, "gameover"},
      {(board.gameover or board.clear) and index in board.bomb, "bomb"},
      {cell == :marked, "flag"},
      {cell not in [:nomark, :marked], "open"}
    ]
    |> Enum.reduce("cell", fn({condition, str}, acc) ->
        if condition, do: acc <> " " <> str, else: acc
      end)

    end
```
条件と文字列のペアーを列挙して、それをreduceで処理。
無理してreduceで処理できる形式のリストを作ってる感があります。

# リファクタリン第二弾

一つの関数では一つの処理。この原則にのっとって、文字列の結合は、append_str_if関数に追い出してみます。
reduceの場合と構造的には同じですが、この名前の関数を使って、書いてみました。
すんなり書けます。
doctestもつけておきます。

```elixir:tutorial2.ex
defmodule Tutorial002 do
  @moduledoc """
  Documentation for `Tutorial002`.
  """

  @doc """
      iex> Tutorial002.get_class_string(%{
      ...>   clear: false, gameover: false,bomb: [],
      ...>   cells: {:nomark}
      ...>   },0)
      "cell"
      iex> Tutorial002.get_class_string(%{
      ...>   clear: true, gameover: false,bomb: [],
      ...>   cells: {0}
      ...>   },0)
      "cell clear open"
      iex> Tutorial002.get_class_string(%{
      ...>   clear: false, gameover: true,bomb: [],
      ...>   cells: {0}
      ...>   },0)
      "cell gameover open"
      iex> Tutorial002.get_class_string(%{
      ...>   clear: true, gameover: false,bomb: [0],
      ...>   cells: {0}
      ...>   },0)
      "cell clear bomb open"
      iex> Tutorial002.get_class_string(%{
      ...>   clear: false, gameover: false,bomb: [],
      ...>   cells: {:marked}
      ...>   },0)
      "cell flag"
      iex> Tutorial002.get_class_string(%{
      ...>   clear: false, gameover: false,bomb: [],
      ...>   cells: {:nomark, :marked}
      ...>   },1)
      "cell flag"

  """
  def get_class_string(board, index) do
    cell = elem(board.cells, index)
    "cell"
    |> append_str_if(board.clear, "clear")
    |> append_str_if(board.gameover, "gameover")
    |> append_str_if((board.gameover or board.clear) and index in board.bomb, "bomb")
    |> append_str_if(cell == :marked, "flag")
    |> append_str_if(cell not in [:nomark, :marked], "open")
  end

  def append_str_if(acc, true, str) do
    acc <> " " <> str
  end

  def append_str_if(acc, false, _str) do
    acc
  end

end
```

`|>`使うと、Elixirらしくなるってわけではないけど、この例は、うまくはまった。

append_str_if()のような部品を用意しないといけないのがちょっと不満。もしかしてkernelの関数やライブラリーに助けになるのがあるのかも？

Elixirって楽しい