20
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ElixirAdvent Calendar 2022

Day 17

(初学者向け)Elixirのパターンマッチを本当に理解する為の例

Last updated at Posted at 2022-12-16

Elixirを学び始めると、慣れている人がパターンマッチを上手に使いこなして、短いコードで複数の処理を行っている事に気が付きます。

初学者だと処理内容がよく分からず、解説資料も少ないので、相談する相手がいないと分からないままで終わることもあります。
そんな時に少しでも救いになればと思い、この例を示します。
Elixirのパターンマッチを理解する為の一助になればと思います。

iexで試してみて下さい。
例えば、こんなマップがあったとします。
(アトムのキーだけを含んだマップには特別な構文があり、以下のように記述できます。)

iex> map = %{name: "apple", price: 200, taste: "sweet", crolor: "red"} 
%{crolor: "red", name: "apple", price: 200, taste: "sweet"}

マップの値は以下のように取得します。2つの取得方法があります。

iex> map[:name] 
"apple"
iex> map.name 
"apple"

この辺りはよく分かります。

しかし、パターンマッチさせた場合はどうでしょうか?

iex> %{name: apple_name} = map
%{crolor: "red", name: "apple", price: 200, taste: "sweet"}

apple_nameと入力した場合、何が表示されるか分かりまか?
分からない人、間違えた人は読み進めてみてください。

答えは、こちらです。

iex> apple_name
"apple"

これがパターンマッチの神髄だと思います。
何が起きているかというと、map変数の中のマップから、%{name: apple_name}に合うものを探して、右側のたくさんデータのあるマップの中から、name:キーがマッチするname: "apple"を抽出し、右側の"apple"を、左側の変数apple_nameに束縛しています。

初学者を悩ませるのが、左側のマップのキーの値が一致しないと、右側を束縛してくれないということです。
パターンマッチなら、どちらかがマッチすれば取得できるのではないかと思ってしまうかもしれませんが、
あくまで、キーのパターンマッチです。
だから、以下のような入力はエラーになります。

iex> %{name2: apple} = map      
** (MatchError) no match of right hand side value: %{crolor: "red", name: "apple", price: 200, taste: "sweet"}
    (stdlib 4.0.1) erl_eval.erl:496: :erl_eval.expr/6
    iex:6: (file)
iex> %{name2 apple} = map  
** (CompileError) iex:6: undefined function name2/1 (there is no such import)
iex> %{name2: "apple"} = map       
** (MatchError) no match of right hand side value: %{crolor: "red", name: "apple", price: 200, taste: "sweet"}
    (stdlib 4.0.1) erl_eval.erl:496: :erl_eval.expr/6
    iex:10: (file)

もう少し分かりやすくパターンマッチを表すと、このような形で、左右のマップのキーname:が一致する右側の値"apple"を、左側の変数apple_nameに束縛します。

iex> %{name: apple_name} = %{name: "apple", price: 200, taste: "sweet", crolor: "red"} 
%{crolor: "red", name: "apple", price: 200, taste: "sweet"}
iex> apple_name
"apple"

パターンマッチということなら、左右逆でもいけるのか?と考えるのですが、NGです。

iex> %{name: "apple", price: 200, taste: "sweet", crolor: "red"}  = %{name: apple_name}
** (MatchError) no match of right hand side value: %{name: "apple"}
    (stdlib 4.0.1) erl_eval.erl:496: :erl_eval.expr/6
    iex:10: (file)

左側はパターンマッチする側、右側はパターンマッチされる側と決まっています。

まとめると、
 ・左側はパターンマッチする側、右側はパターンマッチされる側
 ・キーが一致する値の方をマッチさせる仕組みである

パターンマッチが強力なのは、他の言語で例えるなら、「検索(もしくは条件分岐)と変数代入」を一度に行える点だと思います。
この例では、name:というキーでデータ群から同じものを検索して取得できる"apple"という値をapple_nameに束縛しています。パターンマッチは、これだけの処理を非常にシンプルに書くことができます。

また、以下の例のように、マップの中の値がリストになっているケースは、パターンマッチする際によく登場します。以下の変数apple_propertyの中には、リストが束縛されています。もっとデータ構造が複雑なマップや構造体を入れることも可能です。

iex> map = %{apple: [price: 200, taste: "sweet", crolor: "red"], orange: [price: 300, taste: "sweet", crolor: "orange"] , grape: [price: 600, taste: "verysweet", crolor: "purple"]} 
%{
  apple: [price: 200, taste: "sweet", crolor: "red"],
  grape: [price: 600, taste: "verysweet", crolor: "purple"],
  orange: [price: 300, taste: "sweet", crolor: "orange"]
}
iex> %{apple: apple_property} = map 
%{
  apple: [price: 200, taste: "sweet", crolor: "red"],
  grape: [price: 600, taste: "verysweet", crolor: "purple"],
  orange: [price: 300, taste: "sweet", crolor: "orange"]
}
iex> apple_property
[price: 200, taste: "sweet", crolor: "red"]
20
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?