2009-01-15 12 views
9

Ich entwickle ein Erlang-System und mit wiederkehrenden Problemen mit der Tatsache, dass Datensätze Kompilierzeit Pre-Prozessor-Makros (fast) sind, und dass sie zur Laufzeit nicht manipuliert werden können ... Im Grunde arbeite ich mit einem Eigenschaftsmuster, bei dem Eigenschaften zur Laufzeit Objekten auf dem Frontend (AS3) hinzugefügt werden. Im Idealfall würde ich dies mit einer Liste auf der Erlang-Seite widerspiegeln, da es ein grundlegender Datentyp ist, aber dann Datensätze in QCL [zum Abfragen von ETS-Tabellen] nicht möglich wäre, da ich sie genau verwenden muss, welche Datensatzeigenschaft I will abfragen über ... Ich habe mindestens 15 Spalten in der Tabelle "larges", also ist es einfach nur hässlich, sie alle in einer großen switch-Anweisung aufzuführen (Fall X).Erlang und Laufzeit Rekord Einschränkungen

hat jemand irgendwelche Ideen, wie man das elegant löst? vielleicht einige eingebaute Funktionen zum Erstellen von Tupeln mit entsprechenden Signaturen für die Verwendung bei der Mustererkennung (für QLC)?

dank

+0

Es würde helfen, wenn Sie einen Pseudo-Code Beispiel von dem, was Sie gerne tun könnten, geben. – archaelus

Antwort

0

Ich bin nicht sicher, ob ich Ihr Problem vollständig verstehen, aber ich habe von den Aufzeichnungen zu proplists in den meisten Fällen bewegt. Sie sind viel flexibler und viel langsamer. Bei der Verwendung von (d) ets verwende ich normalerweise ein paar Datensatzfelder für die Grobselektion und überprüfe dann die Proplisten auf den verbleibenden Datensätzen für eine detaillierte Auswahl.

+0

Proplists sind praktisch, wenn Sie sie nie pattern müssen (mein Haupteinsatzgebiet ist Optionslisten als 'proplists: get_value/3' erlaubt Ihnen, einen Wert zu erhalten oder einen Standard in einem Vorgang zu verwenden. Wenn Sie jemals ein Muster erstellen möchten übereinstimmen, jedoch benötigen Sie Datensätze für den benannten Steckplatzzugriff. – archaelus

4

Es klingt, als ob Sie in der Lage sein möchten, etwas wie get_record_field(Field, SomeRecord) zu tun, wobei Field zur Laufzeit durch Benutzerschnittstellenkennung bestimmt wird.

Sie haben Recht, dass Sie dies nicht im Standard erlang tun können, da Datensätze und die record_info-Funktion zur Kompilierzeit erweitert und eliminiert werden.

Es gibt ein paar Lösungen, die ich benutzt oder angeschaut habe. Meine Lösung ist wie folgt:

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

Eine Version davon (das Beispiel Laufzeit Zugriff auf die #dns_rec und #dns_rr Datensätze aus inet_dns.hrl gibt), die hier zur Verfügung stellt ist: rec_test.erl


Sie können auch erweitern Dieses dynamische Feld-Lookup für die dynamische Generierung von Matchspecs zur Verwendung mit ets:select/2 oder mnesia:select/2 wie unten gezeigt:

%% 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 hat auch eine parse_transform geschrieben, Exprecs, die das mehr oder weniger automatisch für Sie tut. Ich habe es nie ausprobiert, aber Ulfs Code ist normalerweise sehr gut.


1

ich dieses Problem lösen (in Entwicklung) durch die Parsen verwenden Transformation Tools die .hrl-Dateien zu lesen und Hilfsfunktionen zu erzeugen.

Ich schrieb eine tutorial darauf bei Trap Exit.

Wir verwenden es die ganze Zeit um Match-Spezifikationen zu generieren. Das Schöne ist, dass Sie nichts über den aktuellen Stand der Aufzeichnung zur Entwicklungszeit wissen müssen.

Aber sobald Sie in der Produktion sind, ändern sich die Dinge!Wenn Ihr Datensatz die Basis einer Tabelle ist (im Gegensatz zur Definition eines Feldes in einer Tabelle), ist das Ändern eines zugrunde liegenden Datensatzes schwieriger (um es vorsichtig auszudrücken!).