ErlangMap密钥模式匹配

zi8p0yeb  于 2022-12-08  发布在  Erlang
关注(0)|答案(2)|浏览(182)

我对Erlang比较新,我有一个类似#{“a/.+":“v1”,“B/c/.+"的Map:“v2”}
我想通过输入键“a/d”来匹配“a/.+”并获得“v1”。
当键是正整数,输入键是正则表达式时,很容易模式化,我如何实现它呢?

afdcj2ne

afdcj2ne1#

对于这种情况,必须使用map。其思想是使用regex来查找与输入匹配的键,然后返回Map的值。未优化版本使用re:run/2,而不编译模式。

  1. L1 = [{"a/.+", "v1"}, {"b/c/.+", "v2"}],
  2. LResult1 = lists:foldl(fun({K, V}, ListIn) ->
  3. Match = re:run("a/d", K),
  4. case Match of
  5. nomatch -> ListIn;
  6. _ -> ListIn ++ [{K, V}]
  7. end
  8. end,
  9. [],
  10. L1
  11. ),
  12. case LResult1 of
  13. [] ->
  14. no_matching_rule;
  15. _ ->
  16. Hd1 = hd(LResult1),
  17. {K1, V1} = Hd1,
  18. V1
  19. end.

优化版本可以通过在使用之前编译一次regex模式来实现。

  1. {ok, Mp1} = re:compile("a/.+"),
  2. {ok, Mp2} = re:compile("b/c/.+"),
  3. L2 = [{Mp1, "v1"}, {Mp2, "v2"}],
  4. LResult2 = lists:foldl(fun({K, V}, ListIn) ->
  5. Match = re:run("a/d", K),
  6. case Match of
  7. nomatch -> ListIn;
  8. _ -> ListIn ++ [{K, V}]
  9. end
  10. end,
  11. [],
  12. L2
  13. ),
  14. case LResult2 of
  15. [] ->
  16. no_matching_rule;
  17. _ ->
  18. Hd2 = hd(LResult2),
  19. {K2, V2} = Hd2,
  20. V2
  21. end.

在Erlang shell中粘贴以上任何代码都将生成"v1"
上面的代码假设一个输入可以有多个匹配的模式,但是只有第一个模式会作为输出输出。

展开查看全部
z31licg0

z31licg02#

If you want to find all the keys in the map that match your string, you will need to iterate over the whole map:
a.erl:

  1. -module(a).
  2. -compile(export_all).
  3. find(String, Map) ->
  4. MyMatchFun = fun (RegexKey, Val, Acc) ->
  5. case re:run(String, RegexKey) of
  6. nomatch -> Acc;
  7. {match, _} -> [Val | Acc]
  8. end
  9. end,
  10. maps:fold(MyMatchFun, _Acc=[], Map).

In the shell:

  1. 34> c(a).
  2. a.erl:2:2: Warning: export_all flag enabled - all functions will be exported
  3. % 2| -compile(export_all).
  4. % | ^
  5. {ok,a}
  6. 35> Map.
  7. #{"a/.+" => "v1","a/b/.+" => "v3","b/c/.+" => "v2"}
  8. 36> a:find("a/d", Map).
  9. ["v1"]
  10. 36> a:find("b/c/dd", Map).
  11. ["v2"]
  12. 38> a:find("a/b/cc", Map).
  13. ["v3","v1"]

If there is only one key in the map that will match your string, then you can stop iterating over the map as soon as you find a match:
a.erl:

  1. -module(a).
  2. -compile(export_all).
  3. find(String, Map) ->
  4. I = maps:iterator(Map),
  5. match_for(String, I).
  6. match_for(String, I) ->
  7. case maps:next(I) of
  8. none -> %% then you have reached the end of the Map
  9. no_keys_in_map_matched_string;
  10. {RegExKey, Val, NewI} ->
  11. case re:run(String, RegExKey) of
  12. nomatch -> match_for(String, NewI); %% continue iterating over the Map looking for a match
  13. {match, _} -> Val %% found a match, so return the associated value
  14. end
  15. end.

In the shell:

  1. 27> c(a).
  2. a.erl:2:2: Warning: export_all flag enabled - all functions will be exported
  3. % 2| -compile(export_all).
  4. % | ^
  5. {ok,a}
  6. 28> Map = #{"a/.+" => "v1", "b/c/.+" => "v2"}.
  7. 29> a:find("a/d", Map).
  8. "v1"
  9. 30> a:find("a/b/cc", Map).
  10. "v1"
  11. 31> a:find("b/c/dd", Map).
  12. "v2"
  13. 32> a:find("z", Map).
  14. no_keys_in_map_matched_string
展开查看全部

相关问题