Can multiple guards be used in an erlang ets match spec?

1k views Asked by At

I would like to construct a match spec to select the first element from a tuple when a match is found on the second element, or the second element when the first element matches. Rather than calling ets:match twice, can this be done in one match specification?

2

There are 2 answers

2
mpm On BEST ANSWER

Yes.

In documentation there is example of is_integer(X), is_integer(Y), X + Y < 4711, or [{is_integer, '$1'}, {is_integer, '$2'}, {'<', {'+', '$1', '$2'}, 4711}].

If you are using fun2ms just write funciton with two clauses.

fun({X, Y}) when in_integer(X)
                 andalso X > 5 ->
       Y;

   ({X, Y}) when is_integer(Y)
                 andalso Y > 5 ->
       X.

But you could also create two MatchFunctions. Each consist {MatchHead, [Guard], Return}.

Match head basically tells you how your data looks (is it a tuple, how many elements ...) and assigns to each element match variable $N where N will be some number. Lets say are using two-element tuples, so your match head would be {'$1', '$2'}.

Now lets create guards: For first function we will assume something simple, like first argument is integer greater than 10. So first guard will be {is_integer, '$2'}, and second {'>', '$2', 5}. In both we use firs element of our match head '$2'. Second match function would have same guards, but with use of '$1'.

And at last the return. Since we would like to just return one element, for first function it will be '$1', and for second '$2' (returning tuple is little more complicated, since you would have to wrap it in additional on-element tuple).

So finally, when put together it gives us

[ _FirstMatchFunction = {_Head1 = {'$1', '$2'},
                         _Guard1 = [{is_integer, '$2},
                                    {'>', '$2', 5}],     % second element matches
                         _Return1 = [ '$1']},            % return first element             
  _SecondMatchFunction = {_Head2 = {'$1', '$2'},
                          _Guard2 = [{is_integer, '$1},
                                     {'>', '$1', 5}],      % second element matches
                          _Return2 = [ '$2']} ]            % return first element

Havent had too much time to test it all, but it should work (maybe with minor tweaks).

0
BlackMamba On
-export([main/0]).
-include_lib("stdlib/include/ms_transform.hrl").
main() ->
    ets:new(test, [named_table, set]),
    ets:insert(test, {1, a, 3}),
    ets:insert(test, {b, 2, false}),
    ets:insert(test, {3, 3, true}),
    ets:insert(test, {4, 4, "oops"}),

    io:format("~p~n", [ets:fun2ms(fun({1, Y, _Z}) -> Y;
                     ({X, 2, _Z}) -> X end)]),

    ets:select(test, ets:fun2ms(fun({1, Y, _Z}) -> {first_match, Y};
                   ({X, 2, _Z}) -> {second_match, X}
                end)).

The output is:

[{{1,'$1','$2'},[],['$1']},{{'$1',2,'$2'},[],['$1']}]
[{first_match,a},{second_match,b}]