Редактирование динамических записей Erlang

Я храню некоторые данные в mnesia и хотел бы иметь возможность изменять большинство задействованных значений.

Наивный

change(RecordId, Slot, NewValue) ->
    [Rec] = do(qlc:q([X || X <- mnesia:table(rec), X#rec.id =:= RecordId])),
    NewRec = Rec#rec{Slot=NewValue},
    F = fun() -> mnesia:write(NewRec) end,
    {atomic, Val} = mnesia:transaction(F),
    Val.

не делает этого; компилятор жалуется, что Slot не является atom или _. Есть ли способ выразить общую функцию редактирования слота, как указано выше, или я застряну, определяя целую кучу change_slot?

Немного лучший подход — вытащить части insert и find.

atomic_insert(Rec) ->
    F = fun() -> mnesia:write(Rec) end,
    {atomic, Val} = mnesia:transaction(F),
    Val.

find(RecordId) -> 
    [Rec] = do(qlc:q([X || X <- mnesia:table(rec), X#rec.id =:= RecordId])),
    Rec.

change(RecordId, name, NewValue) ->
    Rec = find(RecordId),
    NewRec = Rec#rec{name=NewValue},
    atomic_insert(NewRec);
change(RecordId, some_other_property, NewValue) ->
    Rec = find(RecordId),
    NewRec = Rec#rec{some_other_property=NewValue},
    ...

но там все еще есть немного дублирования кода. Есть ли способ абстрагировать этот шаблон? Существует ли устоявшаяся методика, позволяющая редактировать записи? Есть идеи вообще?


person Inaimathi    schedule 30.05.2012    source источник


Ответы (3)


Другой способ использования записи, которая на самом деле является кортежем:

change(RecordId, Index, NewValue) ->
    [Rec] = do(qlc:q([X || X <- mnesia:table(rec), X#rec.id =:= RecordId])),
    NewRec = setelement(Index, Rec, NewValue),
    F = fun() -> mnesia:write(NewRec) end,
    {atomic, Val} = mnesia:transaction(F),
    Val.

который вы могли бы использовать следующим образом:

5> Val = record:change(id58, #rec.name, new_value).

Это также «чистое» использование записей в виде кортежей, поскольку вы используете синтаксис #rec.name для поиска индекса поля в кортеже. Именно поэтому этот синтаксис был добавлен.

person rvirding    schedule 31.05.2012
comment
Я закончил тем, что сделал что-то похожее на это. В качестве примечания для людей, читающих вопрос, element/2 также был полезно здесь. - person Inaimathi; 01.06.2012

Поскольку записи представлены кортежами, вы можете попробовать использовать операции с кортежами для установки отдельных значений.

-module(rec).
-export([field_num/1, make_rec/0, set_field/3]).
-record(rec, {slot1, slot2, slot3}).

make_rec() ->
  #rec{slot1=1, slot2=2, slot3=3}.

field_num(Field) ->
  Fields = record_info(fields, rec),
  DifField = fun (FieldName) -> Field /= FieldName end,
  case length(lists:takewhile(DifField, Fields)) of
    Length when Length =:= length(Fields) ->
      {error, not_found};
    Length ->
      Length + 2
  end.

set_field(Field, Value, Record) ->
  setelement(field_num(Field), Record, Value).

set_field вернет обновленную запись:

Eshell V5.9.1  (abort with ^G)
1> c(rec).
{ok,rec}
2> A = rec:make_rec().
{rec,1,2,3}
3> B = rec:set_field(slot3, other_value, A).
{rec,1,2,other_value}
person kjw0188    schedule 30.05.2012

Вы также можете определить change как макрос (особенно если он используется только внутри модуля):

-define(change(RecordId, Slot, NewValue),
        begin
            [Rec] = do(qlc:q([X || X <- mnesia:table(rec), X#rec.id =:= RecordId])),
            NewRec = Rec#rec{Slot=NewValue},
            F = fun() -> mnesia:write(NewRec) end,
            {atomic, Val} = mnesia:transaction(F),
            Val
        end).

Применение:

test(R, Id) ->
    ?change(Id, name, 5).

С помощью макроса вы также можете передать _ в качестве поля (хорошо для сопоставления с образцом).

person Ed'ka    schedule 31.05.2012