Динамическое сопоставление с образцом - PullRequest
6 голосов
/ 21 октября 2010

Как мне выполнить динамическое сопоставление с образцом в 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 выше, чтобы вернуть массив элементов в массиве, которые соответствуют шаблону.

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

ТКС заранее.

Ответы [ 4 ]

5 голосов
/ 21 октября 2010

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

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"}]
2 голосов
/ 22 октября 2010

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

Шаблоны как таковые не существуют в 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.
0 голосов
/ 21 октября 2010

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

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

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

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

  2. Заполните данные из Array в ETS и используйте спецификации соответствия , чтобы получить данные

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

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

0 голосов
/ 21 октября 2010

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

...