2012-05-30 149 views
4

我正在存储一些数据在mnesia,我想能够改变大部分涉及的值。Erlang动态记录编辑

天真

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不是​​或_。有没有一种方法可以像上面那样表示一般的插槽编辑功能,还是我会被定义为一大堆change_slot s?

一个稍好一点的办法是拉出insertfind

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}, 
    ... 

,但还是有一点重复代码出现的。有什么方法可以将这种模式抽象出来吗?是否有一个确定的技术来允许编辑记录?任何想法一般?

+0

请参阅http://www.erlang.org/doc/reference_manual/records.html中的第9.8节“记录的内部表示”。字段名称仅在编译时可用,所以您不能在#rec语法中使用字段名称的变量。 record_info()函数可能对你正在尝试做的事情有所帮助。 – RichardC

回答

2

使用的另一种方式,一个记录是一个真正的元组是:

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语法来查找元组中字段的索引。这是添加此语法的原因。

+0

我最终做了类似的事情。作为阅读问题的人的一个提示,['element/2'](http://www.erlang.org/doc/man/erlang.html#element-2)在这里也很有用。 – Inaimathi

3

由于记录由元组表示,因此可以尝试使用元组操作来设置各个值。

-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} 
3

您还可以定义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). 

使用宏,您也可以将_作为字段(适用于模式匹配)。