Open6

[Elixir] 引数によるパターンマッチ

snamiki1212snamiki1212

Elixiriでの引数によるパターンマッチのパターンがいろいろあるので遭遇したものをまとめておく

snamiki1212snamiki1212

引数でのstructにてパターンマッチしつつ、値とのパターンマッチ

defmodule A do
  def f1(%{a: a = "1st"}), do: IO.puts("this is 1st / " <> a)
  def f1(%{a: a}), do: IO.puts("this is 2nd / " <> a)
  def f1(_), do: raise("Cannot catch")
end

# iex ----
iex> A.f1(%{a: "1st"})
this is 1st / 1st

iex> A.f1(%{a: "2nd :D"})
this is 2nd / 2nd :D

iex> A.f1(%{a_x: "3rd :/"})
** (RuntimeError) Cannot catch
  • Reduxみたいなtype によって処理を変えたいときに使えそう
snamiki1212snamiki1212
defmodule A do
  def f2(%{a: "1st" = a}), do: IO.puts(a)
  def f2(%{a: a = "2nd"}), do: IO.puts(a)
end

# iex ---
iex> A.f2 %{a: "1st"}
1st

iex> A.f2 %{a: "2nd"}
2nd
  • 引数にて変数への束縛をする場合は、左右のどちらに置いても変わらない
snamiki1212snamiki1212
defmodule D do
  use TypedStruct

  typedstruct do
    field :name, String.t(), enforce: true
    field :age, integer()
  end
end

defmodule X do
  def f1(d = %D{age: age}) do
    IO.inspect(d)
  end
end

# iex ---
iex> X.f1 %D{name: "name"}
%D{age: nil, name: "name"}

%D{}はoptionalな要素としてD.ageを持ち、引数に%D{age: age} を取ったときの挙動

  • ageにはnilが入る。構造体的にパターンマッチされないかと思ってた。
  • そのため、データ構造の厳格性という観点でパターンマッチはできない
  • structのレベルでenforce: true にするかvalidatorでチェックしないといけない
snamiki1212snamiki1212
iex> d1 = %{name: "NAME", age: 9}
%{age: 9, name: "NAME"}

iex> d2 = %D{name: "NAME", age: 9}
%D{age: 9, name: "NAME"}

iex> d3 = %D{name: "NAME", age: 9}
%D{age: 9, name: "NAME"}

iex> d1 == d2
false

iex> d2 == d3
true

# 無理くりstructを生成
iex> d4 = %{name: "NAME", age: 9, __struct__: D}
%D{age: 9, name: "NAME"}

iex> d4 == d2
true

前提として、名前付き構造体と無名構造体ではパターンマッチしない。


defmodule D do
  use TypedStruct

  typedstruct do
    field :name, String.t(), enforce: true
    field :age, integer()
  end
end

defmodule C do
  def fd2(param = %D{}), do: IO.puts("OK")
end

# iex ---
iex> C.fd2 %D{name: "NAME", age: 9}
OK

iex> C.fd2 %{name: "NAME", age: 9}
** (FunctionClauseError) no function clause matching in C.fd2/1

そのため、引数のパターンマッチにてdefining structとundefinining struct はパターンマッチはしない