Erlang和运行时记录限制

标签 erlang runtime

我正在开发一个Erlang系统,并经常遇到这样的问题:记录几乎是编译时的预处理器宏,并且它们无法在运行时进行操作...
基本上,我正在使用一种属性模式,其中在运行时将属性添加到前端(AS3)上的对象。理想情况下,我会在Erlang端用一个列表来反射(reflect)这一点,因为它是基本的数据类型,但是然后不可能在QCL中使用记录[查询ETS表],因为要使用它们,我必须具体说说我是哪个记录属性要查询...我在bigs表中至少有15列,因此将它们全部列在一个巨大的switch语句(X的情况)中简直很难看。

有谁知道如何优雅地解决这个问题?也许一些内置函数可以创建带有适当签名的元组,以用于模式匹配(用于QLC)?

谢谢

最佳答案

听起来您希望能够执行类似get_record_field(Field, SomeRecord)的操作,其中Field在运行时由用户界面代码说来确定。

没错,因为记录和record_info函数在编译时会被扩展和删除,因此您无法在标准erlang中执行此操作。

我已经使用或研究了几种解决方案。我的解决方案如下:(该示例使运行时可以从#dns_rec访问#dns_rrinet_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

您还可以将此动态字段查找扩展到动态生成的matchspec,以与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的代码通常非常好。

关于Erlang和运行时记录限制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/447188/

相关文章:

erlang - 如何将具有有效 Erlang 表达式的字符串转换为抽象语法树 (AST)?

string - "average length of the sequences in a fasta file": Can you improve this Erlang code?

javascript - 在运行时获取 node.js 版本

java - 在java中运行内部exe

android - 没有语言运行时的基于 JVM 的语言

erlang - 为什么 Dialyzer 无法捕获这个简单的错误?

erlang - Lager - 解析转换

multithreading - Erlang 的并发模型究竟是什么?

validation - grails-我需要在运行时定义验证

typescript - 如何在 TypeScript 中检查运行时的对象类型?