假设我有记录:
-record(foo, {bar}).
我想要做的是能够将记录名称作为参数传递给函数,并获取新记录。该函数应该是通用的,以便它能够接受任何记录,如下所示。
make_record(foo, [bar], ["xyz"])
实现这样的功能时,我试过这个:
make_record(RecordName, Fields, Values) ->
NewRecord = #RecordName{} %% this line gives me an error: syntax error before RecordName
是否可以将记录名称用作参数?
答案 0 :(得分:8)
如果在编译期间无权访问记录,则无法使用记录语法。
但是因为在编译过程中简单地将记录转换为元组,所以我们很容易手动构建它们:
-record(some_rec, {a, b}).
make_record(Rec, Values) ->
list_to_tuple([Rec | Values]).
test() ->
R = make_record(some_rec, ["Hej", 5]), % Dynamically create record
#some_rec{a = A, b = B} = R, % Access it using record syntax
io:format("a = ~p, b = ~p~n", [A, B]).
或者,如果您在编译时列出了该函数应该能够构造的所有记录,您也可以使用字段名称:
%% List of record info created with record_info macro during compile time
-define(recs,
[
{some_rec, record_info(fields, some_rec)}
]).
make_record_2(Rec, Fields, Values) ->
ValueDict = lists:zip(Fields, Values),
% Look up the record name and fields in record list
Body = lists:map(
fun(Field) -> proplists:get_value(Field, ValueDict, undefined) end,
proplists:get_value(Rec, ?recs)),
list_to_tuple([Rec | Body]).
test_2() ->
R = make_record_2(some_rec, [b, a], ["B value", "A value"]),
#some_rec{a = A, b = B} = R,
io:format("a = ~p, b = ~p~n", [A, B]).
使用第二个版本,您还可以进行一些验证,以确保使用正确的字段等。
动态处理记录时要记住的其他有用构造是#some_rec.a
表达式,它计算a
中some_rec
字段的索引和element(N, Tuple)
赋予元组和索引的函数返回该索引中的元素。
答案 1 :(得分:4)
这是不可能的,因为记录是仅编译时的结构。在编译时,它们被转换为元组。因此编译器需要知道记录的名称,因此您不能使用变量。
你也可以使用一些解析变换魔法(见exprecs)来创建记录构造函数和访问器,但这种设计似乎走错了方向。 如果您需要动态创建类似记录的内容,则可以使用某些结构,例如键值list或dict。
答案 2 :(得分:0)
要涵盖所有情况:如果您有字段和值但不一定按正确的顺序排列,则可以使您的函数获取record_info(fields, Record)
的结果, Record
是你想要制作的唱片的原子。然后它将具有要使用的有序字段名称。并且记录只是第一个插槽中具有原子名称的元组,因此您可以通过这种方式构建它。这是我如何从JSON字符串构建任意浅记录(未经过彻底测试,未经过优化,但经过测试和工作):
% Converts the given JSON string to a record
% WARNING: Only for shallow records. Won't work for nested ones!
%
% Record: The atom representing the type of record to be converted to
% RecordInfo: The result of calling record_info(fields, Record)
% JSON: The JSON string
jsonToRecord(Record, RecordInfo, JSON) ->
JiffyList = element(1, jiffy:decode(JSON)),
Struct = erlang:make_tuple(length(RecordInfo)+1, ""),
Struct2 = erlang:setelement(1, Struct, Record),
recordFromJsonList(RecordInfo, Struct2, JiffyList).
% private methods
recordFromJsonList(_RecordInfo, Struct, []) -> Struct;
recordFromJsonList(RecordInfo, Struct, [{Name, Val} | Rest]) ->
FieldNames = atomNames(RecordInfo),
Index = index_of(erlang:binary_to_list(Name), FieldNames),
recordFromJsonList(RecordInfo, erlang:setelement(Index+1, Struct, Val), Rest).
% Converts a list of atoms to a list of strings
%
% Atoms: The list of atoms
atomNames(Atoms) ->
F = fun(Field) ->
lists:flatten(io_lib:format("~p", [Field]))
end,
lists:map(F, Atoms).
% Gets the index of an item in a list (one-indexed)
%
% Item: The item to search for
% List: The list
index_of(Item, List) -> index_of(Item, List, 1).
% private helper
index_of(_, [], _) -> not_found;
index_of(Item, [Item|_], Index) -> Index;
index_of(Item, [_|Tl], Index) -> index_of(Item, Tl, Index+1).
简要说明:JSON代表一些关键:对应于我们正在尝试构建的记录中的字段:值对的值对。我们可能无法获得正确顺序的键:值对,因此我们需要传入的记录字段列表,以便我们可以将值插入元组中的正确位置。