LoginSignup
15
3

Elixir Erlang match_spec の文法

Last updated at Posted at 2023-11-11

はじめに

ElixirErlang には Match Specifications (match_spec) という便利な機能が備わっています。独特な文法をしているので頭の整理をします。

Erlang Term Storage (ETS) を使うときに、:ets.insert/2:ets.lookup/2:ets.tab2list/1 などがよく利用されると思います。

match_spec を使用すると、:ets.select/2:ets.select_delete/2 などの強力な絞り込み機能を利用できます。

Match Specifications

  • パターン照合して何かを一致させようとするプログラム
  • :ets.select/2 などで ETS テーブル内のオブジェクトを検索したりするために使用可能
  • 関数を呼び出すより効率的だが、表現力は限られている

Match 式(Match Expression)の大まかな構成

Match 式は三つの要素(Match Head, Match Conditions, Match Body)を持つタプルのリストです。

@type match_expression ::
  [{match_head, match_conditions, match_body}, ...]

通常はリスト要素のタプルは一つだけだと思います。タプルが複数の Match 式はまだ見たことがありません。

Match 対象(Match Target Term)

match 対象は ETS テーブルの行のタプル({ key, value1, value2, ...})です。

変数(Match Variable)

変数は :"$整数" の形で表現されます。整数の部分は 0 ~ 100,000,000 (1e+8) の整数です。これらの規則に沿わない場合は未定義となります。

Match Head に限り、何にでもマッチする特殊変数 :_ が使用できます。

Match 式のタプルの第1要素(Match Head)

パターン照合のパターンを定義する部分になります。テーブルの各行と照合し、$整数 変数に束縛します。

@type match_head ::
  {term | :"$整数" | :_, ...}

第一要素がキーで第二要素が値と見るとわかりやすいです。

{:"$1", :"$2"}

{:_, 123}

{:"$1", %{name: :"$2", group: "fukuokaex"}}

Match 式のタプルの第2要素(Match Conditions)

パターンに一致した行に対する絞り込み条件を定義する部分(ガード節)になります。各絞り込み条件は評価の後 true を返すことを期待します。

Match Head で束縛された :"$整数" 変数をここで使用することができます。

@type match_conditions ::
  [match_condition, ...] | []

@type match_condition ::
  {guard_function}
  | {guard_function, condition_expression, ...}
[]

[{:"/=", :"$123", :hoge}]

[
  {:andalso, {:is_integer, :"$123"},
  {:andalso, {:>=, :"$123", 2}, {:"=<", :"$123", 5}}}
]

Match 式のタプルの第3要素(Match Body)

結果を格納するデータ構造を指定する部分です。

@type match_body ::
  [condition_expression, ...]
[:"$_"]

[:"$$"]

[:"$1"]

Match 式の基本的な例

練習用の ETS テーブルを作ります。

ETSの例(値がシンプルな場合)
:ets.new(MyEts1, [:set, :named_table])
:ets.insert(MyEts1, [hoge: 1, fuga: 2, piyo: 3, foo: "3", bar: "4", baz: "5"])
:ets.tab2list(MyEts1)

簡単な絞り込みの例です。

キーが:hogeでない行を抽出
:ets.select(MyEts1, [{
  # テーブルの行にマッチして束縛
  {:"$123", :_}, # キーを変数`:"$123"`に束縛。値は無視。
  # 絞り込み条件
  [
    {:"/=", :"$123", :hoge}
  ],
  # 結果のデータ構造
  [:"$_"] # テーブルの行を返すおまじない。
}])
# 結果: [piyo: 3, fuga: 2, baz: "5", bar: "4", foo: "3"]
値が整数で、2以上5以下の行を抽出
:ets.select(MyEts1, [{
  # テーブルの行にマッチして束縛
  {:_, :"$123"}, # キーは無視。値を変数:"$123"に束縛。
  # 絞り込み条件
  [
    {:andalso, {:is_integer, :"$123"},
    {:andalso, {:>=, :"$123", 2}, {:"=<", :"$123", 5}}}
  ],
  # 結果のデータ構造
  [:"$_"] # テーブルの行を返すおまじない。
}])
# 結果: [piyo: 3, fuga: 2]
指定された値にマッチする行を抽出
:ets.select(MyEts1, [{
  # テーブルの行にマッチして束縛
  {:_, "3"}, # キーは無視。値を"3"と指定。
  # 絞り込み条件
  [],
  # 結果のデータ構造
  [:"$_"] # テーブルの行を返すおまじない。
}])
# 結果: [foo: "3"]

Match 式の戻り値の例

練習用の ETS テーブルをもう一つ作ります。今度は値を Map にします。

ETSの例(値が複雑な場合)
:ets.new(MyEts2, [:set, :named_table])
:ets.insert(MyEts2, [
  {1, %{ name: "hoge", group: :sakura}},
  {2, %{ name: "fuga", group: :sumire}},
  {3, %{ name: "piyo", group: :sakura}}
])
:ets.tab2list(MyEts2)
テーブルの行
:ets.select(MyEts2, [{
  {:_, %{ group: :sakura}},
  [],
  [:"$_"]
}])
# 結果:
# [
#   {3, %{group: :sakura, name: "piyo"}},
#   {1, %{group: :sakura, name: "hoge"}}
# ]
束縛された変数すべて
:ets.select(MyEts2, [{
  {:"$1", %{ name: :"$2", group: :sakura}},
  [],
  [:"$$"]
}])
# 結果:
# [
#   [3, "piyo"],
#   [1, "hoge"]
# ]
任意の値
:ets.select(MyEts2, [{
  {:_, %{ name: :"$1", group: :sakura}},
  [],
  [:"$1"]
}])
# 結果: ["piyo", "hoge"]
任意のタプル
:ets.select(MyEts2, [{
  {:"$1", %{ name: :"$2", group: :sakura}},
  [],
  [{{"闘魂", :"$1", :"$2"}}]
}])
# 結果:
# [
#   {"闘魂", 3, "piyo"},
#   {"闘魂", 1, "hoge"}
# ]
任意のマップ
:ets.select(MyEts2, [{
  {:"$1", %{ name: :"$2", group: :sakura}},
  [],
  [%{id: :"$1", name: :"$2", motto: "元氣があればなんでもできる"}]
}])
# 結果:
# [
#   %{id: 3, name: "piyo", motto: "元氣があればなんでもできる"},
#   %{id: 1, name: "hoge", motto: "元氣があればなんでもできる"}
# ]
真偽値
:ets.select(MyEts2, [{
  {:"$1", %{ group: :"$2"}},
  [{:==, :"$2", :sakura}],
  [true]
}])
# 結果: [true, true]

:ets.select_count(MyEts2, [{
  {:"$1", %{ group: :"$2"}},
  [{:==, :"$2", :sakura}],
  [true]
}])
# 結果: 2

Match 式を生成するツール

Match 式を生成するマクロ等を提供するパッケージがいくつかあるようです。

さいごに

match_spec のドキュメントがわかりにくくて理解に時間がかかりました。

ひょっとすると一旦慣れて仕舞えばそんなに難しくないのかもしれません。

本記事は autoracex #255 の成果です。ありがとうございます。

toukon-qiita-macbook_20230912_091808.jpg

15
3
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
15
3