如何在Erlang中进行动态模式匹配?
我有功能过滤器/ 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
一些没有任何成功的想法......
答案 0 :(得分:5)
进行一点文档研究:
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"}]
答案 1 :(得分:2)
为什么你想要一个字符串中的模式有什么特殊原因?
Erlang中不存在这样的模式,它们实际上只能出现在代码中。另一种方法是使用与ETS match
和select
相同的约定,并编写自己的匹配函数。这真的很简单。 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.
答案 2 :(得分:0)
您可以使用lists:filter/2
来执行过滤部分。将字符串转换为代码是另一回事。所有模式都是{atom, _}
的形式吗?如果是这样,您可以存储原子并将其传递给列表的闭包参数:filter。
答案 3 :(得分:0)
有几种可能性出现在脑海中,具体取决于模式的动态程度以及模式中需要的功能:
如果您需要完全符合erlang模式和的语法,则模式不会经常更改。您可以创建匹配的源代码并将其写入文件。使用compile:file
创建二进制文件并使用code:load_binary
加载。
优势:非常快速匹配
缺点:模式更改时的开销
将数据从Array
填入ETS并使用match specifications获取数据
可能还有某种方法可以使用qlc,但我没有详细研究过。
在任何情况下,如果来自不受信任的来源,请小心清理匹配数据!