【问题标题】:Erlang Dynamic Record EditingErlang 动态记录编辑
【发布时间】:2012-05-30 18:14:03
【问题描述】:

我在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_slots 上?

稍微好一点的方法是取出 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},
    ...

但是那里仍然有一些代码重复。有没有办法抽象出这种模式?是否存在允许编辑记录的既定技术?一般有什么想法吗?

【问题讨论】:

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

标签: dynamic erlang record


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      【解决方案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 语法在元组中查找字段的索引一样。这就是添加此语法的原因。

      【讨论】:

      • 我最终做了类似的事情。作为对阅读问题的人的说明,element/2 在这里也很有用。
      猜你喜欢
      • 2016-09-08
      • 2013-06-18
      • 1970-01-01
      • 2014-10-23
      • 1970-01-01
      • 2020-08-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多