Динамическое сопоставление шаблонов

как я могу сделать динамическое сопоставление шаблонов в Erlang?

Supose у меня есть функция filter / 2:

filter(Pattern, Array)

где Pattern-строка с шаблоном, который я хочу сопоставить (e.g "{book, _ }" или "{ebook, _ }") типизированный пользователем и массив представляет собой массив гетерогенных элементов (e.g {dvd, "The Godfather" } , {book, "The Hitchhiker's Guide to the Galaxy" }, {dvd, "The Lord of Rings"} и т. д.), То я хотел бы filter / 2 выше, чтобы вернуть массив элементов в массиве, который соответствует шаблону.

Я пробовал некоторые идеи с erl_eval без какого-либо успех...

ТКС заранее.

4 ответов


С небольшим изучением документации:

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

есть ли какая-то особая причина, почему вы хотите шаблон в строке?

шаблоны как таковые не существуют в Erlang, они действительно могут возникать только в коде. Альтернативой является использование тех же соглашений, что и с ETS match и select и напишите свою собственную функцию соответствия. На самом деле все очень просто. Конвенция ETS использует термин для представления шаблона, где атомы '', '', etc используются в качестве переменных, которые могут быть связаны и протестированы, и '_' является переменной безразличия. Таким образом, ваш пример шаблонов станет:

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

это, пожалуй, самый эффективный способ сделать это. Здесь есть возможность использовать спецификации соответствия, но это усложнит код. Это зависит от того, насколько сложное соответствие вам нужно.

EDIT: Добавляю Без комментариев код для части 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 ''
        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.

можно использовать lists:filter/2 для выполнения фильтрующей части. Преобразование строки в код-это другое дело. Есть все закономерности в виде {atom, _}? Если это так, вы можете сохранить атом и передать его в аргумент закрытия списков:filter.


несколько возможностей приходят на ум, в зависимости от того, насколько динамичны шаблоны и какие функции вам нужны в ваших шаблонах:

  1. Если вам нужен именно синтаксис шаблонов erlang и шаблон не меняется очень часто. Вы можете создать соответствующий исходный код и записать его в файл. Использовать compile:file чтобы создать двоичный файл и загрузить его с помощью code:load_binary.

    • преимущество: очень быстро соответствие

    • недостаток: накладные расходы при изменении шаблона

  2. штуки данные из Array в ETS и использовать матч технические характеристики выйти

    • можно использовать fun2ms, чтобы помочь создать спецификацию матч. Но fun2ms обычно используется в качестве трансфора синтаксического анализа во время компиляции. Существует также режим, используемый оболочкой, который может быть сделан для работы со строками возможно, с помощью парсера. Подробнее см. ms_transform
  3. там также может быть какой-то способ использовать угловую систему быстрой загрузки QLC

в любом случае будьте осторожны, чтобы санировать соответствующие данные, если они поступают из ненадежных источников!