【问题标题】:How to do pattern matching on map keys in function heads in elixir如何在长生不老药的功能头中对映射键进行模式匹配
【发布时间】:2016-11-08 08:02:30
【问题描述】:

我似乎找不到在函数头中对地图键进行模式匹配的方法。有没有办法做到这一点?我想要做的是根据地图中是否已经存在某个键来运行不同的代码(并且还想避免 if/else 等)

这就是我的代码的样子

def my_func(key, %{key => _} = map), do: ...

这给了我这个错误

** (CompileError) map key match 内变量 key 非法使用,map 只能通过 ^key 匹配现有变量

当然我也试过用^

def my_func(key, %{^key => _} = map), do: ...

然后给出

** (CompileError) 未绑定变量 ^key

我在 Windows 8.1 机器上使用 elixir 1.3.1/erlang 19.0 x64。 感谢阅读!

【问题讨论】:

    标签: pattern-matching elixir


    【解决方案1】:

    您可以根据具体问题和您使用的 Elixir 版本尝试多种方法:

    • 地图模式匹配
    • 检查给定键是否存在 (Elixir
    • 检查给定键是否存在 (Elixir >= 1.10)

    地图模式匹配

    如果你可以用你需要的键进行模式匹配:

    defmodule Test do
      def my_func(%{"a" => value}), do: {:a, value}
      def my_func(%{"b" => value}), do: {:b, value}
      def my_func(_), do: :error
    end
    

    然后在IEx中:

    iex(1)> Test.my_func(%{"a" => 1})
    {:a, 1}
    iex(2)> Test.my_func(%{"b" => 2})
    {:b, 2}
    

    子句的顺序也很重要,例如如果您尝试匹配 %{"b" => 2} 但您有以下映射 %{"a" => 1, "b" => 2},则键 "a" 将首先匹配,因为在第一个子句中:

    iex(3)> Test.my_func(%{"a" => 1, "b" => 2})
    {:a, 1}
    

    如果您想为每个可以匹配的键生成一些东西,我推荐一种不同的方法。例如,如果您想将函数映射到这些键:

    defmodule Test0 do
      def my_op({"times_2", value}), do: {"times_2", value * 2}
      def my_op({"times_3", value}), do: {"times_3", value * 3}
      def my_op({key, value}), do: {key, value}
    
      def my_func(m) do
        Enum.map(m, &my_op/1) |> Enum.into(%{})
      end
    end
    

    所以你会得到以下结果:

    iex(1)> Test0.my_func(%{"times_2" => 2, "times_3" => 3, "whatever" => 42})
    %{"times_2" => 4, "times_3" => 9, "whatever" => 42}
    

    检查给定键是否存在(Elixir

    您无法将键与变量进行模式匹配。问题是编译器需要生成代码来搜索它还不知道的东西。当你进行模式匹配时,你通常会给编译器一些关于它将接收什么的提示。对于地图中的键,提示是键本身。在这种情况下,即使指出第一个参数应该是要查找的关键,对于编译器来说也是不够的。所以你的方法应该是使用 if 语句:

    defmodule Test1 do
      def my_func(k, m) do
        if Map.has_key?(k, m) do
          ... do something when key is found ...
        else
          ... do something when key is not found ...
        end
      end
    end
    

    检查给定密钥是否存在 (Elixir >= 1.10)

    Elixir 1.10 终于添加了可以用来解决这个问题的守卫is_map_key/2

    defmodule Test2 do
      def my_func(key, map) when is_map_key(map, key) do
        ... key is found ...
      end
    
      def my_func(_, _) do
        ... key is not found ...
      end
    end
    

    这肯定会解决编译问题:)

    我希望这能回答你的问题。

    【讨论】:

    • 您好,感谢您的回答。但我可能应该澄清my_func(key, _) 中的键是在运行时确定的变量,在编译时不知道
    • 好的,现在我部分理解了你想要做什么。你的函数到底是做什么的?
    • 嗯,我认为添加函数体没有关系,但是结束代码会大致做到“如果key已经存在触发警告,否则执行foo并将其添加到地图中”
    • 嘿,你是对的,模式匹配发生在编译时......所以这毕竟是不可能的,需要走 if/else 的方式。感谢您的宝贵时间!
    猜你喜欢
    • 2014-03-16
    • 1970-01-01
    • 2018-11-24
    • 1970-01-01
    • 1970-01-01
    • 2016-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多