[Erlang 0006] Erlang中的record與宏 中我們提到過Record是一個編譯時的功能,在Erlang VM中並沒有專門的數據類型.在線上解決問題有時候會遇到要在shell中使用record,那么就有兩個選擇:1.在shell中構造record定義,如果能構造record有了record的定義編寫ets:match的匹配模式就方便多了; 2.直接使用record對應的tuple結構;
方法一 使用rd命令
Eshell V5.9 (abort with ^G)
1> rd(film ,{ director, actor, type, name,imdb}).
film
2> F =#film{}.
#film{director = undefined,actor = undefined,
type = undefined,name = undefined,imdb = undefined}
3> F#film.type.
undefined
4> F#film.type=23.
* 1: illegal pattern
5> F2 =F#film{type=23}.
#film{director = undefined,actor = undefined,type = 23,
name = undefined,imdb = undefined}
方法二使用rr命令
rr命令可以加載模塊中的record定義,我們把record放在一個模塊中:
-module(records).
-record(book,{name, id, item01, item02, item03, item04, item05, item06, item07, item08, item09, item10 } ).
-record(film ,{ director, actor, type, name,imdb}).
注意:rr方法是支持通配符的,比如rr("*")
我們在shell中編譯並嘗試加載其中的record:
Eshell V5.9 (abort with ^G)
1> c(records).
records.erl:2: Warning: record book is unused
records.erl:19: Warning: record film is unused
{ok,records}
2> rr(records).
[book,film]
3> F =#film{}.
#film{director = undefined,actor = undefined,
type = undefined,name = undefined,imdb = undefined}
4>
方法三 最方便的方案 user_default
上面兩種方法一種是手工定義,一種是手工加載定義,有更理想的方案么?有!
仔細閱讀一下Erlang shell的文檔,我們可以看到下面這段: http://www.erlang.org/documentation/doc-5.4.12/lib/stdlib-1.13.11/doc/html/shell.html
If a command (local function call) is not recognized by the shell, an attempt is first made to find the function in the module user_default, where customized local commands can be placed. If found, then the function is evaluated. Otherwise, an attempt is made to evaluate the function in the module shell_default. The module user_default must be explicitly loaded.
There is some support for reading and printing records in the shell. During compilation record expressions are translated to tuple expressions. In runtime it is not known whether a tuple actually represents a record. Nor are the record definitions used by compiler available at runtime. So in order to read the record syntax and print tuples as records when possible, record definitions have to be maintained by the shell itself. The shell commands for reading, defining, forgetting, listing, and printing records are described below. Note that each job has its own set of record definitions. To facilitate matters record definitions in the modules shell_default and user_default (if loaded) are read each time a new job is started.
我們就使用user_default來解決這個問題!!!我們准備一下測試的文件:
%% File: records.hrl
-record(book,{ name, id, item01, item02, item03, item04, item05, item06, item07, item08, item09, item10 } ).
-record(film ,{ director, actor, type, name,imdb}).
-record(foo,{ id, name,bar}).
%% File: user_default.erl
-module(user_default).
-include("records.hrl").
-compile(export_all).
get_meta() ->
user_default:module_info().
get_timestamp() ->
{M, S, _} = erlang:now(),
M * 1000000 + S.
編譯一下user_default.erl,編譯之后我們啟動Erlang Shell,可以看到不僅record已經加載了,而且user_default中定義的方法也可以直接訪問.
Eshell V5.9 (abort with ^G)
1> rl(). %% 在shell中可以使用rl()查看已經定義的record.
-record(book,{name, id, item01, item02, item03, item04, item05, item06, item07, item08, item09, item10 } ).
-record(film,{director,actor,type,name,imdb}).
-record(foo,{id,name,bar}).
ok
2> get_timestamp().
1325308014
3> F=foo#{}.
* 1: syntax error before: '{'
3> F=#foo{}.
#foo{id = undefined,name = undefined,bar = undefined}
4>
補遺:
在centos環境,在user_default.beam所在的目錄啟動erl 使用rl()查看,record定義沒有加載,只返回了ok
我懷疑是user_default的加載時機有問題,就修改了~/.erlang 文件,添加了加載邏輯 code:load_abs("/data/op_server/user_default").
這樣修改之后,還是沒有效果,然后我懷疑是不是~/.erlang文件沒有執行;於是我故意把里面的代碼改錯,再啟動erl報錯,這樣可以確認這個文件的內容確實執行了
立濤的回答:
code:load_abs("..../user_default").
這一行刪除也沒有關系。
只要HOME目錄下有user_default.beam
成立濤 19:53:36
編譯的時候加上 erlc +debug_info user_default.erl
然后把user_default.beam放到當前目錄就ok了。
我看了下代碼 :)
方法四 不定義直接使用tuple
[Erlang 0006] Erlang中的record與宏 我們說過record在Erlang的內部實現是還是一個tuple.在shell中我們可以使用等效的tuple.
4> F2 =#film{director=2012}.
#film{director = 2012,actor = undefined,type = undefined,
name = undefined,imdb = undefined}
5> F2 == {film , 2012, undefined, undefined, undefined, undefined} .
true
方法五 Match Specifications And Records (Dynamically!)
這是trapexit上提供的一個解決方案,稍微繞一點,主要是{ok,Tree}=epp:parse_file("myheader.hrl",["./"],[])解析頭文件進行預處理,然后生成reocrd的元數據module文件,再利用工具類來動態生成match specification;

1 %%%-------------------------------------------------------------------
2 %%% File : record_util.erl
3 %%% Author : Gordon Guthrie gordon@hypernumbers.com
4 %%% Description : utilities for manipulating records
5 %%%
6 %%% Created : 2 Sep 2008 by Gordon Guthrie
7 %%%-------------------------------------------------------------------
8 -module(make_ms_util).
9
10 -include("myheader.hrl").
11
12 -export([make/0]).
13
14 -define(MODULENAME,"ms_util2").
15
16 make() ->
17 {ok,Tree}=epp:parse_file("myheader.hrl",["./"],[]),
18 Src=make_src(Tree),
19 ok=file:write_file(?MODULENAME++".erl",list_to_binary(Src)).
20
21 make_src(Tree) -> make_src(Tree,[]).
22
23 make_src([],Acc) -> make_src2(Acc,[],[]);
24 make_src([{attribute,_,record,Record}|T],Acc) -> make_src(T,[Record|Acc]);
25 make_src([_H|T],Acc) -> make_src(T,Acc).
26
27 make_src2([],Acc1,Acc2) -> top_and_tail(Acc1,Acc2);
28 make_src2([H|T],Acc1,Acc2) -> {NewAcc1,NewAcc2}=expand_rec(H),
29 make_src2(T,[NewAcc1|Acc1],[NewAcc2|Acc2]).
30
31 expand_rec({Name,Def}) -> expand_fields(Name,Def,1,[]).
32
33 expand_fields(Name,[],N,Acc) -> {mk2(Name,N-1),lists:reverse([mk(Name)|Acc])};
34 expand_fields(Name,[{record_field,_,{atom,_,F},_}|T],N,Acc) ->
35 expand_fields(Name,T,N+1,[mk(Name,F,N)|Acc]);
36 expand_fields(Name,[{record_field,_,{atom,_,F}}|T],N,Acc) ->
37 expand_fields(Name,T,N+1,[mk(Name,F,N)|Acc]);
38 expand_fields(Name,[H|T],N,Acc) -> expand_fields(Name,T,N+1,Acc).
39
40 %% mk2/1 builds the no of fields fns
41 mk2(Name,N) -> "no_of_fields("++atom_to_list(Name)++") -> "++
42 integer_to_list(N)++";\n".
43
44 %% mk/1 builds an error line
45 mk(Name) -> "get_index("++atom_to_list(Name)++",F) -> "++
46 "exit({error,\"Record: "++atom_to_list(Name)++
47 " has no field called \"++atom_to_list(F)});\n".
48
49 mk(Name,Field,N) ->
50 "get_index("++atom_to_list(Name)++","++
51 atom_to_list(Field)++")-> "++integer_to_list(N)++";\n".
52
53 top_and_tail(Acc1,Acc2)->
54 Top="%% This module automatically generated - do not edit\n"++
55 "\n"++
56 "%%% This module provides utilities for use in building\n"++
57 "%%% match specifications from records\n"++
58 "\n"++
59 "-module("++?MODULENAME++").\n"++
60 "\n"++
61 "-export([get_index/2,no_of_fields/1]).\n"++
62 "\n",
63 Tail1="no_of_fields(Other) -> exit({error,\"Invalid Record Name: \""++
64 "++Other}).\n\n\n",
65 Tail2="get_index(Record,_Field) -> exit({error,\""++
66 "Invalid Record Name: \"++Record}).\n",
67 Top++lists:flatten(Acc1)++Tail1++lists:flatten(Acc2)++Tail2.
詳情點擊這里: http://www.trapexit.org/Match_Specifications_And_Records_%28Dynamically%21%29
一個簡單的問題,只要願意去想,答案不止一個;