Erlang的原子(atom)在匹配中有着重要作用,它兼顾了可读性和运行效率。 通过atom,可以实现很多灵活高效的应用。
例:动态生成模块名和函数名,动态调用函数。
在很多非FP语言里,我们经常是组合字符串来生成函数名,然后赋给一个变量来达到动态调用函数的目的。
但是在Erlang里,把字符串变量当作函数名来用是不行的。下面就来看Erlang动态调用函数的示例。
-module(test).
-compile(export_all).
%% F = integer()
mytest(Fid, A) ->
Name = "test" ++ integer_to_list(Fid),
Fun = list_to_existing_atom(Name),
test:Fun(A).
test1(A) ->
{ok, A}.
test2(A) ->
{ok, A}.
test3(A) ->
{ok, A}.
运行结果:
Eshell V5.10.2 (abort with ^G)
1> test:mytest(1, "this is test:test1/1").
{ok,"this is test:test1/1"}
2> test:mytest(2, "this is test:test2/1").
{ok,"this is test:test2/1"}
3> test:mytest(3, "this is test:test3/1").
{ok,"this is test:test3/1"}
下面继续看几个例子:
Eshell V5.10.2 (abort with ^G)
1> M = list_to_existing_atom("erlang").
erlang
2> F = list_to_existing_atom("localtime").
localtime
3> M:F().
{{2013,7,29},{13,40,41}}
4> MF = list_to_existing_atom("erlang:localtime").
** exception error: bad argument
in function list_to_existing_atom/1
called as list_to_existing_atom("erlang:localtime")
5> MF = list_to_atom("erlang:localtime").
'erlang:localtime'
6> MF().
** exception error: bad function 'erlang:localtime'
上面通过list_to_existing_atom函数成功读取了erlang和localtime这两个已经存在的atom,而且通过M:F()成功调用了erlang:localtime/0。
但是,list_to_existing_atom("erlang:localtime")却是失败的,它是一个不存在的atom,所以只能用list_to_atom/1,就算是生成了'erlang:localtime'这个atom,通过MF()调用也是失败的。
从上面们可以知道,在Erlang中,已经加载的模块和函数,都有分别导出对应的atom,调用时也是通过atom来中转的。
现在来探索一下list_to_atom函数的底层是怎么实现的。
$ERL_TOP/erts/emulator/beam/bif.c
BIF_RETTYPE list_to_atom_1(BIF_ALIST_1)
{
Eterm res;
char *buf = (char *) erts_alloc(ERTS_ALC_T_TMP, MAX_ATOM_CHARACTERS);
// 把字符串暂存到buf中
int i = intlist_to_buf(BIF_ARG_1, buf, MAX_ATOM_CHARACTERS);
if (i < 0) {
erts_free(ERTS_ALC_T_TMP, (void *) buf);
i = list_length(BIF_ARG_1);
if (i > MAX_ATOM_CHARACTERS) {
BIF_ERROR(BIF_P, SYSTEM_LIMIT);
}
BIF_ERROR(BIF_P, BADARG);
}
// 通过erts_atom_put保存原子,返回一个索引值(一个整数)
// 进一步查看erts_atom_put函数会看到:
// 如果原子已经存在,则直接返回已存在的索引值,
// 如果不存在则保存并生成索引值
res = erts_atom_put((byte *) buf, i, ERTS_ATOM_ENC_LATIN1, 1);
ASSERT(is_atom(res));
erts_free(ERTS_ALC_T_TMP, (void *) buf);
BIF_RET(res);
}
从上面可看出,atom在内部使用时,它是一个数字(索引值)。
atom可以看作是给字符串生成了一个ID,内部使用的是ID值,必要时可以取出它的内容(字符串),例如用于打印输出。
接着看一下list_to_atom的逆过程,atom_to_list/1函数的实现:
#define INDEX_PAGE_SHIFT 10
#define INDEX_PAGE_SIZE (1 << INDEX_PAGE_SHIFT)
#define INDEX_PAGE_MASK ((1 << INDEX_PAGE_SHIFT)-1)
ERTS_GLB_INLINE Atom*
atom_tab(Uint i)
{
// 查找atom
return (Atom *) erts_index_lookup(&erts_atom_table, i);
}
ERTS_GLB_INLINE IndexSlot*
erts_index_lookup(IndexTable* t, Uint ix)
{
// 经过两次指针移动定位到目标位置
return t->seg_table[ix>>INDEX_PAGE_SHIFT][ix&INDEX_PAGE_MASK];
}
/*
* Atom entry.
*/
typedef struct atom {
IndexSlot slot; /* atom的索引值保存在slot->index */
Sint16 len; /* atom字符串的长度 */
Sint16 latin1_chars;
int ord0;
byte* name; /* 原子的名称被保存到这里 */
} Atom;
BIF_RETTYPE atom_to_list_1(BIF_ALIST_1)
{
Atom* ap;
Uint num_chars, num_built, num_eaten;
byte* err_pos;
Eterm res;
#ifdef DEBUG
int ares;
#endif
if (is_not_atom(BIF_ARG_1))
BIF_ERROR(BIF_P, BADARG);
/* read data from atom table */
// 通过atom的索引值找到Atom(结构体)
ap = atom_tab(atom_val(BIF_ARG_1));
if (ap->len == 0)
BIF_RET(NIL); /* the empty atom */
#ifdef DEBUG
ares =
#endif
erts_analyze_utf8(ap->name, ap->len, &err_pos, &num_chars, NULL);
ASSERT(ares == ERTS_UTF8_OK);
// 读取atom的名称(ap->name)并生成字符串(list)返回
res = erts_utf8_to_list(BIF_P, num_chars, ap->name, ap->len, ap->len,
&num_built, &num_eaten, NIL);
ASSERT(num_built == num_chars);
ASSERT(num_eaten == ap->len);
BIF_RET(res);
}
从上面可看到,atom的存储和查找,是通过index和hash来实现的。
atom更深层次的实现,可参见atom.c、index.c、hash.c及external.c。
小结
$ERL_TOP/erts/emulator/beam/atom.names文件中保存了常用的500多个原子,在Eralng启动初始化时会解析这个文件并导入atom。
Erlang的atom是为匹配(比较)而生的,它的作用就是为了匹配更快捷。
atom数量上限是1M,并且不参与GC,一旦创建将永远保存,一旦数量超出上限,VM很快就会宕掉,所以不可滥用atom。
分享到:
相关推荐
Erlang及其应用Erlang及其应用Erlang及其应用
Erlang开发及应用
Erlang应用优化指南 Erlang应用优化指南
erlang的timer和实现机制 Erlang程序设计
Erlang的高级特性和应用Erlang的高级特性和应用
erlang文献及资料汇总 入门资料: erlang中文手册(R11B 文档译文,最适合入门) erlang位运算与二进制解析 erlang二进制高效编程 erlang异常处理详解 开发经验: 面对软件错误构建可靠的分布式系统 编写分布式的 ...
kmp游戏中使用频率较高,所以用erlang代码代码实现,效率还行。
Erlang emulator 实现分析Erlang emulator 实现分析
[奥莱理] 网络应用开发 (Erlang 实现) (英文版) [奥莱理] Building Web Applications with Erlang Working with REST and Web Sockets on Yaws (E-Book) ☆ 出版信息:☆ [作者信息] Zachary Kessin [出版机构] ...
个人学习Erlang对于Erlang处理数据的各种方式的总结,讲述编程语言Erlang的ets,dets,mnesia,mysql,dict,和进程字典的用法,希望能帮助读者在这个总结里学到一些东西.
Erlang零成本实现云计算,为初学者提供参考和学习,并为企业建设云提供帮助
使用Erlang编写出的应用运行时通常由成千上万个轻量级进程组成,并通过消息传递相互通讯。进程间上下文切换对于Erlang来说仅仅 只是一两个环节,比起C程序的线程切换要高效得多得多了。 使用Erlang来编写分布式应用...
erlang入门电子书 erlang编程 Introducing Erlang,作者Simon.St.Laurent
余锋《erlang零成本实现云计算》--淘宝网核心系统技术专家--2010中国软件技术大会.pdf
牛人写的PPT,主要讲述的是ERLANG的一些特点,案例和应用
erlang 安装包
gcm-erlang, 用于Google云消息传递的Erlang应用程序 gcm 软件提供了一个用于 Google Cloud Messaging的Erlang客户机。,你可以对 gcm-erlang做什么:你可以以使用 gcm-erlang:启动几个代表由不同 GCM API keys 定义...
书中兼顾了顺序编程、并发编程和分布式编程,重点介绍如何编写并发和分布式的Erlang程序以及如何在多核CPU上自动加速程序,并深入地讨论了开发Erlang应用中至关重要的文件和网络编程、OTP、ETS和DETS等主题。...