9

私は Erlang システムを開発していますが、レコードはコンパイル時のプリプロセッサ マクロ (ほぼ) であり、実行時に操作できないという問題が再発しています... 基本的に、私はプロパティ パターンで作業しています。プロパティは実行時にフロントエンド (AS3) のオブジェクトに追加されます。理想的には、これを Erlang 側のリストに反映させます。これは基本的なデータ型であるためです。しかし、QCL で [ETS テーブルをクエリするために] レコードを使用することはできません。クエリを実行したい... larges テーブルには少なくとも 15 の列があるため、それらすべてを 1 つの巨大な switch ステートメント (case X of) にリストするのは単純に見苦しいです。

これをエレガントに解決する方法はありますか?パターンマッチング(QLC用)で使用するための適切な署名を持つタプルを作成するための組み込み関数でしょうか?

ありがとう

4

3 に答える 3

4

実行時にユーザーインターフェイスコードによって決定されるget_record_field(Field, SomeRecord)場所のようなことを実行できるようにしたいようです。Field

record_infoレコードと関数がコンパイル時に拡張および削除されるため、標準のerlangではこれを実行できないという点で正しいです。

私が使用または検討したソリューションがいくつかあります。#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から入手できます。


この動的フィールドルックアップを、以下に示すように、ets:select/2またはmnesia:select/2以下に示すように、matchspecsの動的生成に拡張することもできます。

%% 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のコードは通常非常に優れています。


于 2009-02-26T19:35:22.927 に答える
1

この問題 (開発中) を解決するには、解析変換ツールを使用して .hrl ファイルを読み取り、ヘルパー関数を生成します。

Trap Exitチュートリアルを書きました。

マッチスペックを生成するために常に使用しています。すばらしい点は、開発時にレコードの現在の状態について何も知る必要がないことです。

ただし、本番環境に入ると状況が変わります。レコードが (テーブル内のフィールドの定義とは対照的に) テーブルの基礎である場合、基になるレコードを変更することはより困難です (控えめに言っても!)。

于 2009-02-27T00:42:02.317 に答える
0

私はあなたの問題を完全に理解しているかどうかはわかりませんが、ほとんどの場合、レコードからプロップリストに移動しました。それらははるかに柔軟性があり、はるかに低速です。(d)etsの使用私は通常、粗い選択のためにいくつかのレコードフィールドを使用し、詳細な選択のために残りのレコードのプロップリストをチェックします。

于 2009-01-20T22:05:29.380 に答える