Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic pattern matching

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.

like image 662
xboard Avatar asked Oct 21 '10 15:10

xboard


2 Answers

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"}]
like image 71
Hynek -Pichi- Vychodil Avatar answered Nov 15 '22 18:11

Hynek -Pichi- Vychodil


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.
like image 44
rvirding Avatar answered Nov 15 '22 17:11

rvirding