2009-01-15 57 views
9

我正在开发一个Erlang系统,并且遇到了记录是编译时预处理器宏(几乎),并且它们不能在运行时被操纵的事实再发生问题... 基本上,我正在使用属性模式,在运行时将属性添加到前端对象(AS3)。理想情况下,我会在Erlang的一个列表中反映这一点,因为它是一个基本的数据类型,但是然后在QCL中使用记录[查询ETS表]是不可能的,因为要使用它们,我必须具体说明哪个记录属性I想要查询...我在larges表中至少有15列,因此将它们全部列在一个巨大的switch语句中(case X)非常难看。Erlang和运行时间记录的限制

没有人有任何想法如何优雅地解决这个问题?也许有一些内置函数用于创建具有适当签名的元组以用于模式匹配(用于QLC)?

感谢

+0

如果你给你想要什么,能够做一个伪代码示例它会帮。 – archaelus 2009-02-26 19:38:18

回答

0

我不知道我完全理解你的问题,但我已经从记录在大多数情况下,转移到proplists。它们更灵活,速度更慢。使用(d)ets我通常使用几个记录字段进行粗选,然后检查剩余记录中的proplists以进行详细选择。

+0

Proplists是方便,如果你永远不需要模式匹配他们(我的主要用途是选项列表作为`proplists:的get_value/3`允许您检索一个值,或在一个操作中使用默认 如果你想要的图案。但是,您需要记录指定的插槽访问权限。 – archaelus 2009-02-26 19:06:35

4

这听起来像你想能够做一些像get_record_field(Field, SomeRecord)其中Field是由用户界面代码说在运行时确定。

你是对的,你不能在标准erlang中做到这一点,因为记录和record_info函数在编译时被扩展和消除。

我已经使用或看过几种解决方案。我的解决方案如下:(例子给出了从inet_dns.hrl#dns_rec#dns_rr记录运行时访问)

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


您还可以扩展这种动态字段查找动态生成匹配specs与ets:select/2mnesia: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的代码通常非常好。


1

我解决了这个问题(在开发中)通过使用解析变换工具来读取.hrl文件并生成帮助函数。我写了tutorialTrap Exit

我们一直使用它来生成匹配规格。美丽之处在于您不需要知道任何关于在开发时间处的当前状态。

然而,一旦你在生产中的东西改变!如果你的记录是表格的基础(而不是表格中字段的定义),那么改变一个基础记录就更困难了(说得轻松!)。