【问题标题】:Erlang and run-time record limitationsErlang 和运行时记录限制
【发布时间】:2010-10-01 14:57:47
【问题描述】:

我正在开发一个 Erlang 系统,但由于记录是编译时预处理器宏(几乎),并且它们不能在运行时被操作,所以我经常遇到问题...... 基本上,我正在使用属性模式,其中属性在运行时添加到前端(AS3)上的对象。理想情况下,我会在 Erlang 端使用一个列表来反映这一点,因为它是一种基本数据类型,但随后无法使用 QCL [查询 ETS 表] 中的记录,因为要使用它们我必须明确说明我是哪个记录属性想要查询...我在 larges 表中至少有 15 列,因此在一个巨大的 switch 语句(case X of)中将它们全部列出是很丑陋的。

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

谢谢

【问题讨论】:

  • 如果您提供一个伪代码示例来说明您希望能够做什么,将会有所帮助。

标签: erlang runtime


【解决方案1】:

听起来您希望能够执行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


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


【讨论】:

    【解决方案2】:

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

    我在Trap Exit 上写了一个tutorial

    我们一直使用它来生成匹配规范。美妙之处在于您无需了解在开发时记录的当前状态。

    但是,一旦您投入生产,情况就会发生变化!如果您的记录是表的基础(而不是表中字段的定义),那么更改基础记录会更加困难(委婉地说!)。

    【讨论】:

      【解决方案3】:

      我不确定我是否完全理解您的问题,但在大多数情况下,我已从记录转移到 proplists。它们更灵活,也更慢。使用 (d)ets 我通常使用一些记录字段进行粗略选择,然后检查剩余记录上的 proplists 以进行详细选择。

      【讨论】:

      • Proplist 非常方便,如果你从不需要模式匹配它们(我的主要用途是选项列表,proplists:get_value/3 允许你在一个操作中检索一个值或使用默认值。如果你想然而,模式匹配,您需要命名槽访问的记录。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-03-28
      • 2020-04-02
      • 2020-08-16
      • 1970-01-01
      • 1970-01-01
      • 2013-06-25
      • 1970-01-01
      相关资源
      最近更新 更多