web-dev-qa-db-ja.com

空のマップに対するパターン一致機能

パターンマッチで遊んでいると、メソッドのパラメーターを空のマップに対してパターンマッチするのは簡単ではないことがわかりました。私はそれがこのような何かになるだろうと思った:

defmodule PatternMatch do
  def modify(%{}) do
    %{}
  end

  def modify(map) do
    # expensive operation
    %{ modified: "map" }
  end
end

しかし、最初の関数句は任意のマップに一致するようです:

iex> PatternMatch.modify(%{a: "map"})
==> %{}

空のマップを確認する別の方法はありますか?

43
leifg

設計上はこのように機能しますが、確かに一見すると少し混乱する可能性があります。この機能により、すべてのキーを指定しなくても、パターンマッチングを使用してマップを分解できます。例えば:

iex> %{b: value} = %{a: 1, b: 2, c: 3}
%{a: 1, b: 2, c: 3}

iex> value
2

その結果、%{}はどのマップにも一致します。関数内の空のマップと一致させたい場合は、ガード句を使用する必要があります。

defmodule PatternMatch do
  def modify(map) when map == %{} do
    %{}
  end

  def modify(map) do
    # ...
  end
end
75
Patrick Oscity

@PatrickOscityの答え(空のマップに使用します)に加えて、 map_size/1 ガードを使用して、複数のキーを持つマップと一致させることができます。

defmodule PatternMatch do
  def modify(map) when map_size(map) == 0 do
    %{}
  end

  def modify(map) when map_size(map) == 1 do
    #something else
  end

  def modify(map) do
    # expensive operation
    %{ modified: "map" }
  end
end

以下は、Kernel.match?/2を使用してmap_size/1が動作していることを示すiexの出力です。

iex(6)> Kernel.match?(map when map_size(map) == 1, %{})
false
iex(7)> Kernel.match?(map when map_size(map) == 1, %{foo: "bar"})
true
23
Gazler

これまでに提供されたすべてのクールな回答に加えて、帽子または上矢印のように見える 単項ピン演算子 の使用を検討することもできます。関連するドキュメントに記載されているように、変数の接頭辞として使用して変数の値とパターンが一致するようにします。

変数を再バインドするのではなく、既存の変数の値とパターンマッチする場合は、ピン演算子^を使用します

次に例を示します。

defmodule A do
  def determine_map_volume(some_map) do
    an_empty_map = %{}

    some_map
    |> case do
    ^an_empty_map -> :empty  # Application of pin operator
    _ -> :not_empty
    end
  end
end

次のように確認できます。

A.determine_map_volume(%{})
:empty
A.determine_map_volume(%{a: 1})
:not_empty

どの方法を使用するかは、コードを読みやすくするための個人/組織の好みによって異なります。

0
Kevin Johnson