How to match ets:match against a record in Erlang?
Asked Answered
F

3

9

I have heard that specifying records through tuples in the code is a bad practice: I should always use record fields (#record_name{record_field = something}) instead of plain tuples {record_name, value1, value2, something}.

But how do I match the record against an ETS table? If I have a table with records, I can only match with the following:

ets:match(Table, {$1,$2,$3,something}

It is obvious that once I add some new fields to the record definition this pattern match will stop working.

Instead, I would like to use something like this:

ets:match(Table, #record_name{record_field=something})

Unfortunately, it returns an empty list.

Fastidious answered 29/9, 2011 at 8:50 Comment(0)
A
17

The cause of your problem is what the unspecified fields are set to when you do a #record_name{record_field=something}. This is the syntax for creating a record, here you are creating a record/tuple which ETS will interpret as a pattern. When you create a record then all the unspecified fields will get their default values, either ones defined in the record definition or the default default value undefined.

So if you want to give fields specific values then you must explicitly do this in the record, for example #record_name{f1='$1',f2='$2',record_field=something}. Often when using records and ets you want to set all the unspecified fields to '_', the "don't care variable" for ets matching. There is a special syntax for this using the special, and otherwise illegal, field name _. For example #record_name{record_field=something,_='_'}.

Note that in your example you have set the the record name element in the tuple to '$1'. The tuple representing a record always has the record name as the first element. This means that when you create the ets table you should set the key position with {keypos,Pos} to something other than the default 1 otherwise there won't be any indexing and worse if you have a table of type 'set' or 'ordered_set' you will only get 1 element in the table. To get the index of a record field you can use the syntax #Record.Field, in your example #record_name.record_field.

Angadreme answered 29/9, 2011 at 10:33 Comment(2)
After this example: #record_name{f1='$1',f2='$2',record_field=something} you gave the example: #record_name{record_field=something,_='_'}. That makes it seem like writing _='_' once will match all the other fields--but in my attempts you have to write _='_' for each additional field. That means if you change your record definition to have more or fewer fields, then you also have to change your match spec--which obviously is not ideal. The one nice thing is: you don't have to specify the field you are matching, e.g. record_field=something , in the correct position in the tuple...Philippic
...You can specify the field you are matching first, then follow that with the correct number of _='_'. For instance with this record definition -record(user, {id, name, city}), you can use a pattern like this #user{city="London", _='_', _='_'}.Philippic
S
10

Try using

ets:match(Table, #record_name{record_field=something, _='_'})

See this for explanation.

Somatic answered 29/9, 2011 at 10:28 Comment(1)
In recent version OTP (24) ets:match/2 does not work this way, but ets:match_object/2 works fine.Moppet
I
4

Format you are looking for is #record_name{record_field=something, _ = '_'}

http://www.erlang.org/doc/man/ets.html#match-2

http://www.erlang.org/doc/programming_examples/records.html (see 1.3 Creating a record)

Inboard answered 29/9, 2011 at 10:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.