我正在寻找一种在Erlang中使用部分元组的列表中查找元组的方法,类似于Prolog中的函子匹配。例如,我想下面的代码返回true
:
member({pos, _, _}, [..., {pos, 1, 2}, ...])
由于以下错误,此代码不能立即工作:
variable '_' is unbound
是否有一种简单的方法可以达到同样的效果?
对于简单的情况,最好使用前面提到的列表:keymember/3。但是如果你真的需要member
函数,你可以自己实现它,像这样:
member(_, []) ->
false;
member(Pred, [E | List]) ->
case Pred(E) of
true ->
true;
false ->
member(Pred, List)
end.
的例子:
>>> member(fun ({pos, _, 2}) -> true; (_) -> false end, [..., {pos, 1, 2}, ...]).
使用lists:keymember/3代替
您可以使用列表推导式来使用宏:
-define(member(A,B), length([0 || A <- B])>0).
?member({pos, _, _}, [{width, 17, 42}, {pos, 1, 2}, totally_irrelevant]).
它不是很有效(它遍历整个列表),但它是我能想到的最接近原始语法的。
如果你想实际提取匹配的元素,你只需要删除'length'并添加一个变量:
-define(filter(A,B), [_E || A =_E <- B]).
您可以使用列表推导式:
Matches = [ Match || {Prefix, _, _} = Match <- ZeList, Prefix == pos].
另一种可能性是做匹配规范所做的事情,并使用原子'_'
而不是原始_
。然后,您可以编写类似于以下的函数:
member(X, List) when is_tuple(X), is_list(List) ->
member2(X, List).
% non-exported helper functions:
member2(_, []) ->
false;
member2(X, [H|T]) when not is_tuple(H); size(X) =/= size(H) ->
member2(X, T);
member2(X, [H|T]) ->
case is_match(tuple_to_list(X), tuple_to_list(H)) of
true -> true;
false -> member2(X, T)
end.
is_match([], []) ->
true;
is_match(['_'|T1], [_|T2]) ->
is_match(T1, T2);
is_match([H|T1], [H|T2]) ->
is_match(T1, T2);
is_match(_, _) ->
false.
那么,你的调用现在将是:
member({pos, '_', '_'}, [..., {pos, 1, 2}, ...])
这不会让你匹配像{A, A, '_'}
这样的模式(检查前两个元素是否相同),但如果你不需要变量,这应该可以工作。
您还可以扩展它,使用使用类似语法的变量来匹配规范('$1'
, '$2'
等),并进行更多的工作—使用到目前为止看到的变量绑定向is_match
添加第三个参数,然后为它们编写类似于'_'
的子句的函数子句。
当然,这不是最快的方法。需要注意的是,我还没有实际测量过,我希望在使用fun的语言中使用模式匹配将提供更好的性能,尽管它确实使调用站点变得有点冗长。
可以使用ets:match
:
6> ets:match(T, '$1'). % Matches every object in the table
[[{rufsen,dog,7}],[{brunte,horse,5}],[{ludde,dog,5}]]
7> ets:match(T, {'_',dog,'$1'}).
[[7],[5]]
8> ets:match(T, {'_',cow,'$1'}).
[]