模式匹配功能对空地图

时间:2015-10-21 00:01:37

标签: pattern-matching elixir

我正在玩模式匹配,我发现,将方法的匹配参数与空映射进行模式化并不容易。我认为它会是这样的:

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

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

但似乎第一个函数子句匹配任意映射:

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

还有另一种检查空地图的方法吗?

3 个答案:

答案 0 :(得分:72)

它通过设计以这种方式工作,但不可否认,乍一看它可能有点令人困惑。此功能允许您使用模式匹配来解构地图,而无需指定所有键。例如:

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

答案 1 :(得分:22)

除了@ 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

以下是iex使用Kernel.match?/2显示map_size/1的输出结果:

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

答案 2 :(得分:0)

除了目前为止提供的所有很酷的答案,您还可以考虑使用看似帽子或上箭头的unary pin operator。您可以使用它为变量添加前缀,以确保模式与其值匹配,如相关文档中所述:

  

如果要对a进行模式匹配,请使用pin运算符^   现有变量的值而不是重新绑定变量

以下是一个例子:

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

您打算使用哪种方法取决于您的个人/组织偏好,以确保代码的可读性。