ErlangMap密钥模式匹配

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

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

afdcj2ne

afdcj2ne1#

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

L1 = [{"a/.+", "v1"}, {"b/c/.+", "v2"}],
LResult1 = lists:foldl(fun({K, V}, ListIn) ->  
            Match = re:run("a/d", K),
            case Match of
                nomatch -> ListIn;
                      _ -> ListIn ++ [{K, V}]
            end
        end,
        [],
        L1
        ),
case LResult1 of
    [] -> 
        no_matching_rule;
     _ -> 
        Hd1 = hd(LResult1),
        {K1, V1} = Hd1,
        V1
end.

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

{ok, Mp1} = re:compile("a/.+"),
{ok, Mp2} = re:compile("b/c/.+"),
L2 = [{Mp1, "v1"}, {Mp2, "v2"}],
LResult2 = lists:foldl(fun({K, V}, ListIn) ->  
            Match = re:run("a/d", K),
            case Match of
                nomatch -> ListIn;
                      _ -> ListIn ++ [{K, V}]
            end
        end,
        [],
        L2
        ),
case LResult2 of
    [] -> 
        no_matching_rule;
     _ -> 
        Hd2 = hd(LResult2),
        {K2, V2} = Hd2,
        V2
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:

-module(a).
-compile(export_all).

find(String, Map) ->
    MyMatchFun = fun (RegexKey, Val, Acc) -> 
                         case re:run(String, RegexKey) of
                             nomatch -> Acc;
                             {match, _} -> [Val | Acc]
                         end
                 end,
    maps:fold(MyMatchFun, _Acc=[], Map).

In the shell:

34> c(a).              
a.erl:2:2: Warning: export_all flag enabled - all functions will be exported
%    2| -compile(export_all).
%     |  ^

{ok,a}

35> Map.
#{"a/.+" => "v1","a/b/.+" => "v3","b/c/.+" => "v2"}

36> a:find("a/d", Map).
["v1"]

36> a:find("b/c/dd", Map).
["v2"]

38> a:find("a/b/cc", Map).
["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:

-module(a).
-compile(export_all).

find(String, Map) ->
    I = maps:iterator(Map),
    match_for(String, I).

match_for(String, I) ->
    case maps:next(I) of 
        none ->  %% then you have reached the end of the Map
            no_keys_in_map_matched_string;

        {RegExKey, Val, NewI} ->
            case re:run(String, RegExKey) of
                nomatch -> match_for(String, NewI);  %% continue iterating over the Map looking for a match
                {match, _} -> Val   %% found a match, so return the associated value
            end
    end.

In the shell:

27> c(a).                
a.erl:2:2: Warning: export_all flag enabled - all functions will be exported
%    2| -compile(export_all).
%     |  ^

{ok,a}

28> Map = #{"a/.+" => "v1", "b/c/.+" => "v2"}.

29> a:find("a/d", Map).                                         
"v1"

30> a:find("a/b/cc", Map).
"v1"

31> a:find("b/c/dd", Map).
"v2"

32> a:find("z", Map).     
no_keys_in_map_matched_string

相关问题