我正在开发一个Erlang系统并且重复出现的问题是记录是编译时预处理器宏(差不多),并且它们无法在运行时进行操作...基本上,我正在使用属性模式,其中属性在运行时添加到前端(AS3)上的对象。理想情况下,我会在Erlang方面用一个列表来反映这一点,因为它是一个基本的数据类型,但是在QCL [查询ETS表]中使用记录是不可能的,因为使用它们我必须具体说出哪个记录属性我想查询...我在larges表中至少有15列,所以在一个巨大的switch语句中列出它们(案例X)只是简单的丑陋。
有没有人有任何想法如何优雅地解决这个问题?也许一些内置函数用于创建具有适当签名的元组以用于模式匹配(用于QLC)?
谢谢
听起来你想要能够做像get_record_field(Field, SomeRecord)
这样的事情,其中Field
在运行时由用户界面代码说明。
你是对的,你不能在标准的erlang中做这些记录,并且record_info
函数在编译时被扩展和消除。
我使用或查看了几种解决方案。我的解决方案如下:(该示例提供了对来自#dns_rec
的#dns_rr
和inet_dns.hrl
记录的运行时访问)
%% Retrieves the value stored in the record Rec in field Field.
info(Field, Rec) ->
Fields = fields(Rec),
info(Field, Fields, tl(tuple_to_list(Rec))).
info(_Field, _Fields, []) -> erlang:error(bad_record);
info(_Field, [], _Rec) -> erlang:error(bad_field);
info(Field, [Field | _], [Val | _]) -> Val;
info(Field, [_Other | Fields], [_Val | Values]) -> info(Field, Fields, Values).
%% The fields function provides the list of field positions
%% for all the kinds of record you want to be able to query
%% at runtime. You'll need to modify this to use your own records.
fields(#dns_rec{}) -> fields(dns_rec);
fields(dns_rec) -> record_info(fields, dns_rec);
fields(#dns_rr{}) -> fields(dns_rr);
fields(dns_rr) -> record_info(fields, dns_rr).
%% Turns a record into a proplist suitable for use with the proplists module.
to_proplist(R) ->
Keys = fields(R),
Values = tl(tuple_to_list(R)),
lists:zip(Keys,Values).
编译的版本可以在这里找到:rec_test.erl
您还可以将此动态字段查找扩展为动态生成matchspecs,以便与ets:select/2
或mnesia:select/2
一起使用,如下所示:
%% Generates a matchspec that does something like this
%% QLC psuedocode: [ V || #RecordKind{MatchField=V} <- mnesia:table(RecordKind) ]
match(MatchField, RecordKind) ->
MatchTuple = match_tuple(MatchField, RecordKind),
{MatchTuple, [], ['$1']}.
%% Generates a matchspec that does something like this
%% QLC psuedocode: [ T || T <- mnesia:table(RecordKind),
%% T#RecordKind.Field =:= MatchValue]
match(MatchField, MatchValue, RecordKind) ->
MatchTuple = match_tuple(MatchField, RecordKind),
{MatchTuple, [{'=:=', '$1', MatchValue}], ['$$']}.
%% Generates a matchspec that does something like this
%% QLC psuedocode: [ T#RecordKind.ReturnField
%% || T <- mnesia:table(RecordKind),
%% T#RecordKind.MatchField =:= MatchValue]
match(MatchField, MatchValue, RecordKind, ReturnField)
when MatchField =/= ReturnField ->
MatchTuple = list_to_tuple([RecordKind
| [if F =:= MatchField -> '$1'; F =:= ReturnField -> '$2'; true -> '_' end
|| F <- fields(RecordKind)]]),
{MatchTuple, [{'=:=', '$1', MatchValue}], ['$2']}.
match_tuple(MatchField, RecordKind) ->
list_to_tuple([RecordKind
| [if F =:= MatchField -> '$1'; true -> '_' end
|| F <- fields(RecordKind)]]).
Ulf Wiger还编写了一个parse_transform,Exprecs,或多或少会自动为你做这件事。我从未尝试过,但Ulf的代码通常非常好。
我不确定我是否完全理解你的问题,但在大多数情况下我已从记录转移到proplists。它们更灵活,速度更慢。使用(d)ets我通常使用几个记录字段进行粗略选择,然后检查剩余记录上的支持者以进行详细选择。