Динамическое сопоставление шаблонов
как я могу сделать динамическое сопоставление шаблонов в 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.
несколько возможностей приходят на ум, в зависимости от того, насколько динамичны шаблоны и какие функции вам нужны в ваших шаблонах:
-
Если вам нужен именно синтаксис шаблонов erlang и шаблон не меняется очень часто. Вы можете создать соответствующий исходный код и записать его в файл. Использовать
compile:file
чтобы создать двоичный файл и загрузить его с помощьюcode:load_binary
.преимущество: очень быстро соответствие
недостаток: накладные расходы при изменении шаблона
-
штуки данные из
Array
в ETS и использовать матч технические характеристики выйти- можно использовать fun2ms, чтобы помочь создать спецификацию матч. Но fun2ms обычно используется в качестве трансфора синтаксического анализа во время компиляции. Существует также режим, используемый оболочкой, который может быть сделан для работы со строками возможно, с помощью парсера. Подробнее см. ms_transform
там также может быть какой-то способ использовать угловую систему быстрой загрузки QLC
в любом случае будьте осторожны, чтобы санировать соответствующие данные, если они поступают из ненадежных источников!