[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
一個簡單的問題,只要願意去想,答案不止一個;