【问题标题】:Dynamic pattern matching动态模式匹配
【发布时间】:2011-04-28 16:54:50
【问题描述】:

如何在 Erlang 中进行动态模式匹配?

假设我有函数 filter/2 :

filter(Pattern, Array)

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

我用erl_eval 尝试了一些想法,但没有任何成功...

提前通知。

【问题讨论】:

    标签: erlang pattern-matching


    【解决方案1】:

    您可以使用lists:filter/2 来做过滤部分。将字符串转换为代码是另一回事。所有的模式都是{atom, _}的形式吗?如果是这样,您也许可以存储原子并将其传递给列表的闭包参数:过滤器。

    【讨论】:

    • Tks Nathon,但对于我的用例,我真的需要一种动态方式。
    【解决方案2】:

    通过一点点文档研究:

    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"}]
    

    【讨论】:

      【解决方案3】:

      会想到几种可能性,具体取决于模式的动态程度以及您在模式中需要哪些功能:

      1. 如果您需要 erlang 模式的语法并且,则该模式不会经常更改。您可以创建匹配的源代码并将其写入文件。使用compile:file 创建一个二进制文件并使用code:load_binary 加载它。

        • 优点:匹配速度非常快

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

      2. Array中的数据填入ETS并使用match specifications取出数据

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

      无论如何,如果您的匹配数据来自不受信任的来源,请务必小心清理!

      【讨论】:

        【解决方案4】:

        有什么特殊的原因需要字符串中的模式吗?

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

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

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

        编辑: 我为匹配器的一部分添加了不带注释的代码:

        %% 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.
        

        【讨论】:

        • Tks rvirding,我会尽快根据您的建议进行一些研究。
        猜你喜欢
        • 2021-10-23
        • 1970-01-01
        • 2017-02-23
        • 2016-06-08
        • 2010-12-20
        • 2012-06-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多