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?
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}, ...]).
Use lists:keymember/3 instead.
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]).
You could do it using list comprehension:
Matches = [ Match || {Prefix, _, _} = Match <- ZeList, Prefix == pos].
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.
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'}).
[]
Related
Is it possible to programmatically pattern match?
Pattern = {error, '_'},
IsError =
case {error, "foo"} of
Pattern -> true;
_ -> false
end.
I know I can do this with macros, but I have a dynamic list of patterns I would like to match that aren't known ahead of time.
Perhaps the closest you can get is using a compiled match specification, by calling the functions ets:match_spec_compile and ets:match_spec_run:
MS = ets:match_spec_compile([{{error, '_'}, [], ['$_']}]).
Items = [ok, {error, foo}, {error, bar}].
ets:match_spec_run(Items, MS).
This returns the two items in the Items list that match:
[{error,foo},{error,bar}]
To know if particular expression matches specified pattern I can write in erlang something like following:
case <expression> of
<pattern> -> true;
_ -> false
end
For example:
case {1, 2, 3} of
{_, 2, _} -> true;
_ -> false
end
Is there a way to write it in more compact way? Something like:
is_match(<pattern>, <expression>)
No, there's no such construct. You could define a macro to do it:
-define(is_match(Pattern, Expr), case Expr of Pattern -> true; _ -> false end).
another macro :o) -define (IS_MATCH(PAT,EXP), catch(PAT = EXP) == EXP end). with no case, but I am not sure that a catch is better.
Using Erlang, I have the following expression:
{add,{var,a},{mul,{num,2},{var,b}}}
and I am using lists:keymember to see whether the letter b is within the expression as such:
lists:keymember(b,2,[expr])
However, it doesn't look within the third tuple '{mul,{num,2},{var,b}' as that is a separate tuple. Is there a function that will search through the whole tuple and tuples within?
Thanks
As far I as I know there are no such functions. Probably you will have to implement some custom solution using recursion. Here is my example:
-module(test).
-compile(export_all).
find(_, []) -> false;
find(E, T) when is_tuple(T) ->
find(E, tuple_to_list(T));
find(E, [H|T]) ->
case find(E, H) of
false -> find(E, T);
true -> true
end;
find(V, E) -> V == E.
And usage:
1> test:find(b, {add,{var,a},{mul,{num,2},{var,b}}}).
true
2> test:find(b, {add,{var,a},{mul,{num,2},{var,c}}}).
false
Please review your code.
Line1: this is a tree, not a list.
Line2: expr is not a variable.
What you want to do is a visitor function, and you'll have to write it yourself.
A very good start would be to read this.
I'd like to know if there is a function in Erlang can help me know whether an element is in a tuple or not. Like sets:is_element/2.
Tuple = {aaa,bbb,ccc}.
is_element_of_tuple(Tuple, aaa) % => true
is_element_of_tuple(Tuple, ddd) % => false
You can always transform the tuple to a list using tuple_to_list/1:
is_element_of_tuple(Tuple, Element) ->
lists:member(Element, tuple_to_list(Tuple)).
The simple answer is: no there is no function to do this. You have to write your own loop which traverses all the elements of a tuple until it either finds or does not find it. You an either convert the tuple to a list as above or write your own loop, something like:
is_element_of_tuple(E, Tuple) ->
is_element_of_tuple(E, Tuple, 1, tuple_size(Tuple)).
is_element_of_tuple(E, T, I, S) when I =< S ->
case element(I, T) of
E -> true;
_ -> is_element_of_tuple(E, T, I+1, S)
end;
is_element_of_tuple(_, _, _, _) -> false. %Done all the elements
Using a case and matching in this way means we check for exact equality, and it is probably a little faster than using =:= and checking if that returns true or false.
Is there a better way to implement Racket's ormap in Erlang than:
ormap(_, []) -> false;
ormap(Pred, [H|T]) ->
case Pred(H) of
false -> ormap(Pred, T);
_ -> {ok, Pred(H)}
end.
Looks pretty good to me. I'm not sure how smart Erlang is about optimizing these things, but you might want to actually bind the non-false pattern match to a variable, and avoid recomputing Pred(H).
ormap(_, []) -> false;
ormap(Pred, [H|T]) ->
case Pred(H) of
false -> ormap(Pred, T);
V -> {ok, V}
end.
The Racket version doesn't include the ok symbol, but that seems like the Erlangy thing to do so I don't see anything wrong with it. You might similarly expect Pred to return an attached ok symbol for the non-false case, in which case:
V -> V
or
{ok, V} -> {ok, V}
should work.