[翻訳] Elixirノート：Mapのパターンマッチング

Rob Phoenixさんの2016年1月4日付のブログ記事[Notes on Elixir: Pattern-Matching Maps](https://bordeltabernacle.github.io/2016/01/04/notes-on-elixir-pattern-matching-maps.html)の翻訳です。ここのところ立て続けにElixirについてのノートを投稿していて[前回の投稿](https://bordeltabernacle.github.io/2015/12/31/notes-on-elixir-pattern-matching.html)は私の[前回の翻訳](http://qiita.com/HirofumiTamori/items/5a1e55d982f90d8a3dbc)とほぼ同じ内容でした。つまり単純なスカラー型データやリストについての例です。
今回取り挙げられているのはMap型のデータでElixirのパターンマッチングがどうなるか、という例です。

-----

パターンマッチングについての[前回の投稿](https://bordeltabernacle.github.io/2015/12/31/notes-on-elixir-pattern-matching.html)からの続きで、Elixirで主なキー・バリューストアとして使われる`map`型データは、パターンマッチングについては他のデータ構造と全然違う面白い機能を持っています。

`map`はその値のまさにサブセットだけをパターンマッチさせることができます。マッチングのパターン内のキーはマッチさせる対象の中に存在している必要がありますが、`list`や`tuple`の場合のようにマッチング対象とパターンが正確に一致している必要はありません。例えば:

```iex
iex(1)> [a, b] = [1, 2, 3]
** (MatchError) no match of right hand side value: [1, 2, 3]

iex(1)> {a, b} = {1, 2, 3}
** (MatchError) no match of right hand side value: {1, 2, 3}

iex(1)> %{:a => one} = %{:a => 1, :b => 2, :c =>3}
%{a: 1, b: 2, c: 3}
iex(2)> one
1
iex(3)> %{:a => one, :c => three} = %{:a => 1, :b => 2, :c =>3}
%{a: 1, b: 2, c: 3}
iex(4)> three
3
iex(5)> one
1
iex(6)> %{} = %{:a => 1, :b => 2, :c =>3}
%{a: 1, b: 2, c: 3}
iex(7)> %{:d => four} = %{:a => 1, :b => 2, :c =>3}
** (MatchError) no match of right hand side value: %{a: 1, b: 2, c: 3}

iex(8)> %{:a => one, :d => four} = %{:a => 1, :b => 2, :c =>3}
** (MatchError) no match of right hand side value: %{a: 1, b: 2, c: 3}
```

`list`も`tuple`もパターンのデータ構造とマッチしようとするデータ構造、端的には大きさ、が異なっているとマッチングに失敗するのがわかりますね[^1]。ところが`map`では事情が異なります。キーがパターンと、マッチしようとするデータの両方に含まれているならば、それらの大きさが異なっていてもマッチングは成功します。空の`map`であっても[^2]です。
[^1]: 上記の例の最初の２行のことです。
[^2]: 上記の例のiex(6)のところです。

しかしながらパターンに入っているキーがマッチしようとするデータに入っていない場合はマッチングは失敗します。これはたとえ他のキーがマッチした場合であってもです。従ってパターンで使われる全てのキーはマッチしようとするデータに含まれている必要があります。

この仕組みは[Phoenix Framework](http://www.phoenixframework.org/)の中でパラメータを関数に引き渡そうとする際に広く使われています。関数は必要とするデータだけを取り出すことができるのです。

新規ユーザーを生成するための関数はこうなります:

```ex
def create(conn, %{"user" => user_params}) do
  # 変数user_paramを使って何かする
end
```

この関数の第２引数として渡されるmapの中身は実際にはこのようになっています:

```iex
Parameters: %{"_csrf_token" => "UR99GwILHTtzbSBUYRwmBVpdeDY/AAAA3K70jEiO9UhgPVwh+d3WYw==",
              "_utf8" => "✓",
              "user" => %{"name" => "Memphis Minnie",
                          "password" => "[FILTERED]",
                          "username" => "minnie"}}
```

`create`関数は`"user"`キーに対してのみパターンマッチングを行い、対応する値、この場合はまた別のmapですが、をこの関数内で使用される変数`user_params`にバインドします。この関数は基本的に与えられたパラメータのmapからユーザー情報だけを取り出し、必要としているものと関係ない残りのデータは捨ててしまいます。

map全体をマッチしつつ同時にパターンマッチされたmapの部分をマッチすることも可能です。`create`関数の中身を書き換えてみましょう:

```ex
def create(conn, parameters = %{"user" => user_params}) do
  IO.inspect user_params["name"]
  IO.inspect parameters["_utf8"]
  # 変数user_paramを使って何かする
end
```

この関数を使って`parameter`と渡されたパラメータ全体のmapをマッチさせ、`user_params`をパラメータ全体mapの`"user"`キーとマッチさせることで以下のように表示されるはずです。

```iex
[info] POST /users
"Skip James"
"✓"
```

面白いことに`parameters`のパターンマッチングは他の書き方もできます:

```ex
def create(conn, %{"user" => user_params} = parameters) do
  IO.inspect user_params["name"]
  IO.inspect parameters["_utf8"]
  # 変数user_paramを使って何かする
end
```

なぜならここでは`%{"user" => user_params} = parameters`がマッチのパターンであり、マッチさせようとするデータが渡されたパラメータ全体のmapだからです。さらに*パターンの内側で*異なる部分をパターンとマッチさせて、他の変数にバインドすることもできます。私の知る限りこれはより慣用句的に使われている手法で、ほとんどの場合こちらが見られるでしょう。

パターンマッチングは`map`キーバリューストアから値を取り出すのに実に優れた方法を提供してくれます。