动态模式匹配

发布于 2024-09-29 03:24:47 字数 469 浏览 5 评论 0原文

如何在 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" "} 等)然后我希望上面的 filter/2 返回 Array 中与 Pattern 匹配的元素数组。

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

提前谢谢。

How can I do dynamic pattern matching in Erlang?

Supose I have the function filter/2 :

filter(Pattern, Array)

where Pattern is a string with the pattern I want to match (e.g "{book, _ }" or "{ebook, _ }") typed by an user and Array is an array of heterogenous elements (e.g {dvd, "The Godfather" } , {book, "The Hitchhiker's Guide to the Galaxy" }, {dvd, "The Lord of Rings"}, etc) Then I would like filter/2 above to return the array of elements in Array that match Pattern.

I've tried some ideas with erl_eval without any sucess...

tks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

你是年少的欢喜 2024-10-06 03:24:47

通过一点点文档研究:

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

With little bit documentation study:

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"}]
静谧幽蓝 2024-10-06 03:24:47

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

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.

Is there any special reason why you want the pattern in a string?

Patterns as such don't exist in Erlang, they can really only occur in code. An alternative is to use the same conventions as with ETS match and select and write your own match function. It is really quite simple. The ETS convention uses a term to represent a pattern where the atoms '$1', '$2', etc are used as variables which can be bound and tested, and '_' is the don't care variable. So your example patterns would become:

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

This is probably the most efficient way of doing it. There is the possibility of using match specifications here but it would complicate the code. It depends on how complicated matching you need.

EDIT:
I add without comment code for part of the 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.
思念绕指尖 2024-10-06 03:24:47

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

You can use lists:filter/2 to do the filtering part. Converting the string to code is a different matter. Are all the patterns in the form of {atom, _}? If so, you might be able to store the atom and pass that into the closure argument of lists:filter.

无人接听 2024-10-06 03:24:47

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

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

    • 优点:匹配速度非常快

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

  2. Array 填充数据 进入 ETS 并使用匹配规范获取数据

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

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

Several possibilities come to the mind, depending on how dynamic the patterns are and what features you need in your patterns:

  1. If you need exactly the syntax of erlang patterns and the pattern doesnt't change very often. You could create the matching source code and write it to a file. Use compile:file to create a binary and load this with code:load_binary.

    • Advantage: Very fast matching

    • Disadvantage: overhead when pattern changes

  2. Stuff the data from Array into ETS and use match specifications to get out the data

    • You might use fun2ms to help create the match specification. But fun2ms normally is used as a parse transfor during compile time. There is also a mode used by the shell that can be made to work from strings with the help of the parser probably. For details see ms_transform
  3. There might also be some way to use qlc but I didn't look into this in detail.

In any case be careful to sanitize your matching data if it comes from untrusted sources!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文