代码之家  ›  专栏  ›  技术社区  ›  xboard

动态模式匹配

  •  6
  • xboard  · 技术社区  · 14 年前

    如何在二郎进行动态模式匹配?

    supose我有功能过滤器/2:

    filter(Pattern, Array)
    

    其中pattern是一个与我想要匹配的模式的字符串(例如 "{book, _ }" "{ebook, _ }" )由用户键入,数组是一个异质元素数组(例如 {dvd, "The Godfather" } , {book, "The Hitchhiker's Guide to the Galaxy" }, {dvd, "The Lord of Rings"} 等)然后我希望上面的filter/2返回数组中匹配模式的元素数组。

    我试过一些想法 erl_eval 没有任何成功…

    提前TKS。

    4 回复  |  直到 14 年前
        1
  •  5
  •   Hynek -Pichi- Vychodil Paulo Suassuna    14 年前

    通过少量的文档研究:

    Eval = fun(S) -> {ok, T, _} = erl_scan:string(S), {ok,[A]} = erl_parse:parse_exprs(T), {value, V, _} = erl_eval:expr(A,[]), V end,
    FilterGen = fun(X) -> Eval(lists:flatten(["fun(",X,")->true;(_)->false end."])) end,
    filter(FilterGen("{book, _}"), [{dvd, "The Godfather" } , {book, "The Hitchhiker's Guide to the Galaxy" }, {dvd, "The Lord of Rings"}]).
    [{book,"The Hitchhiker's Guide to the Galaxy"}]
    
        2
  •  2
  •   rvirding    14 年前

    有什么特殊的原因让你想要一个字符串中的模式吗?

    这样的模式在Erlang中不存在,它们实际上只能在代码中出现。另一种选择是使用与ETS相同的约定 match select 并编写自己的匹配函数。这真的很简单。ETS公约使用一个术语来表示原子 '$1' , '$2' 等用作可绑定和测试的变量,以及 '_' 是“不在乎”变量。因此,您的示例模式将变成:

    {book,'_'}
    {ebook,'_'}
    {dvd,"The Godfather"}
    

    这可能是最有效的方法。这里有可能使用匹配规范,但这会使代码复杂化。这取决于你需要的匹配有多复杂。

    编辑: 我为Matcher的一部分添加了没有注释的代码:

    %% match(Pattern, Value) -> {yes,Bindings} | no.
    
    match(Pat, Val) ->
        match(Pat, Val, orddict:new()).
    
    match([H|T], [V|Vs], Bs0) ->
        case match(H, V, Bs0) of
            {yes,Bs1} -> match(T, Vs, Bs1);
            no -> no
        end;
    match('_', _, Bs) -> {yes,Bs};                  %Don't care variable
    match(P, V, Bs) when is_atom(P) ->
        case is_variable(P) of
            true -> match_var(P, V, Bs);            %Variable atom like '$1'
            false ->
                %% P just an atom.
                if P =:= V -> {yes,Bs};
                   true -> no
                end
        end.
    
    match_var(P, V, Bs) ->
        case orddict:find(P, Bs) of
            {ok,B} when B =:= V -> {yes,Bs};
            {ok,_} -> no;
            error -> {yes,orddict:store(P, V, Bs)}
        end.
    
        3
  •  0
  •   nmichaels    14 年前

    你可以使用 lists:filter/2 做过滤部分。将字符串转换为代码是另一回事。所有的图案都是 {atom, _} ?如果是这样,您可能能够存储原子并将其传递到lists:filter的闭包参数中。

        4
  •  0
  •   Peer Stritzinger    14 年前

    根据模式的动态性以及模式中需要的功能,可以想到几种可能性:

    1. 如果您确实需要Erlang模式的语法 这种模式不会经常改变。您可以创建匹配的源代码并将其写入文件。使用 compile:file 创建二进制文件并用 code:load_binary .

      • 优势:匹配速度非常快

      • 缺点:模式改变时的开销

    2. 从中填充数据 Array 进入ETS并使用 match specifications 把数据拿出来

      • 你可以使用 fun2ms 以帮助创建匹配规范。但是fun2ms通常在编译时用作解析转换器。shell还使用了一种模式,可以在解析器的帮助下从字符串中工作。详情见 ms_transform
    3. 可能还有一些方法可以使用 qlc 但我没有详细调查过。

    在任何情况下,如果匹配的数据来自不受信任的源,请小心对其进行清理!