Reputation: 5351
I am looking for a way to find tuples in a list in Erlang using a partial tuple, similarly to functors matching in Prolog. For example, I would like to following code to return true
:
member({pos, _, _}, [..., {pos, 1, 2}, ...])
This code does not work right away because of the following error:
variable '_' is unbound
Is there a brief way to achieve the same effect?
Upvotes: 6
Views: 3672
Reputation: 4269
May use 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'}).
[]
Upvotes: 0
Reputation: 7994
You can do it with a macro using a list comprehension:
-define(member(A,B), length([0 || A <- B])>0).
?member({pos, _, _}, [{width, 17, 42}, {pos, 1, 2}, totally_irrelevant]).
It is not very efficient (it runs through the whole list) but it is the closest I can think to the original syntax.
If you want to actually extract the elements that match you just remove 'length' and add a variable:
-define(filter(A,B), [_E || A =_E <- B]).
Upvotes: 2
Reputation: 6378
Another possibility would be to do what match specs do and use the atom '_'
instead of a raw _
. Then, you could write a function similar to the following:
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.
Then, your call would now be:
member({pos, '_', '_'}, [..., {pos, 1, 2}, ...])
This wouldn't let you match patterns like {A, A, '_'}
(checking where the first two elements are identical), but if you don't need variables this should work.
You could also extend it to use variables using a similar syntax to match specs ('$1'
, '$2'
, etc) with a bit more work -- add a third parameter to is_match
with the variable bindings you've seen so far, then write function clauses for them similar to the clause for '_'
.
Granted, this won't be the fastest method. With the caveat that I haven't actually measured, I expect using the pattern matching in the language using a fun will give much better performance, although it does make the call site a bit more verbose. It's a trade-off you'll have to consider.
Upvotes: 0
Reputation: 3637
For simple cases it's better to use already mentioned lists:keymember/3. But if you really need member
function you can implement it yourself like this:
member(_, []) ->
false;
member(Pred, [E | List]) ->
case Pred(E) of
true ->
true;
false ->
member(Pred, List)
end.
Example:
>>> member(fun ({pos, _, 2}) -> true; (_) -> false end, [..., {pos, 1, 2}, ...]).
Upvotes: 4
Reputation: 6366
You could do it using list comprehension:
Matches = [ Match || {Prefix, _, _} = Match <- ZeList, Prefix == pos].
Upvotes: 0