说说erlang tuple和record结构

来源:互联网 发布:c语言打印double 编辑:程序博客网 时间:2024/06/11 13:44

erlang有两种复合结构,tuple和list,两者的区别是tuple子元素的个数是固定不变的,声明后就不能改变了;而list是可变的,可以通过[H|T]来取出或插入新元素。record有点像C/C++里面的结构体,实际上是语法糖,方便我们的开发,代码汇编时转成tuple表达形式。

Tuple

tuple的表示方法如:

{Term1,...,TermN}
下面以例子说明erlang tuple及一些基本操作:
1> A = {1,2}.{1,2}2> tuple_size(A).23> is_tuple(A).true4> tuple_to_list(A).[1,2]5> element(1, A).16> setelement(2, A, 3).{1,3}


Record

record有点像C/C++里面的结构体,表示方法如:

-record(Name, {Field1 [= Value1],               ...               FieldN [= ValueN]}).

下面以例子说明erlang record及一些基本操作:

%% shell下定义 record结构 person%% 等效程序里定义 -record(person, {name, age}).7> rd(person, {name, age}).person8> A1 = #person{name = "john", age = 10}.#person{name = "john",age = 10}9> A2 = A1#person{name = "Lucy"}.#person{name = "Lucy",age = 10}%% record 一次赋值10> X = #person{_ = 1}.#person{name = 1,age = 1}11> element(2, X).112> setelement(2, X, "Tom").#person{name = "Tom",age = 1}

14> P=#person{}.#person{name = undefined,age = undefined}15> is_record(P,person).true16> #person.age.317> #person.name.2

tuple 和 record 有什么关系?

record只是语言程序上的结构,方便我们的开发,在erlang编译的时候会转成tuple处理

%% shell下定义 record结构 person2%% 等效程序里定义 -record(person2, {name = "", age = 1}).23> rd(person2, {name = "", age = 1}).person224> A3 = #person2{name = "Jimmy"}.#person2{name = "Jimmy",age = 1}%% 模式匹配25> {_, Name, _} = A3.#person2{name = "Jimmy",age = 0}26> Name."Jimmy"27> #person2{name = Name2} = A3.#person2{name = "Jimmy",age = 0}28> Name2."Jimmy"

下面,以一个简单的例子,测试tuple和record的汇编代码

-module(test).-export([test/0]).-record(person, {name, age}).test() ->A = {person, "Tom", 1},B = #person{name = "Tom", age = 1},{A,B}.

通过命令erlc -S test.erl 可以生成 test.S的汇编代码

{function, test, 0, 2}.
  {label,1}.
    {line,[{location,"c:/test.erl",7}]}.
    {func_info,{atom,test},{atom,test},0}.
  {label,2}.
    {move, {literal, { {person,"Tom",1} ,{person,"Tom",1}} }, {x,0} }.
    return.

record_info/2

说到record,不得不提record_info/2,这个函数用以获取record的信息,原型:

record_info(Type, Record) ->  integer() | list

Type有两种:size、fields

34> rd(person,{id, name}).person35> record_info(fields, person).[id,name]36> record_info(size, person).3
record_info/2实际上一个语法糖,写个例子tt.erl说明一下:

-module(tt).-compile(export_all).-record(person,{id, name, age}).fields() ->record_info(fields, person).size() ->record_info(size, person).

erlc -S tt.erl

编译这个模块得到 tt.S,这是其中的汇编码:

{function, fields, 0, 2}.
  {label,1}.
    {line,[{location,"tt.erl",5}]}.
    {func_info,{atom,tt},{atom,fields},0}.
  {label,2}.
    {move,{literal, [id,name,age] },{x,0}}.
    return.

{function, size, 0, 4}.
  {label,3}.
    {line,[{location,"tt.erl",8}]}.
    {func_info,{atom,tt},{atom,size},0}.
  {label,4}.
    {move, {integer,4} ,{x,0}}.
    return.

在编译期就直接被erlang优化了


修改tuple结构

R16以后,erlang提供了2个接口用于修改tuple结构。
1、增加tuple元素
erlang:append_element(Tuple1, Term)
> erlang:append_element({one, two}, three).
{one,two,three}

等效于 list_to_tuple(tuple_to_list(Tuple1) ++ [Term]),但性能比后者高 


2、移除tuple元素
erlang:delete_element(Index, Tuple1)
> erlang:delete_element(2, {one, two, three}).
{one,three}


record 的模式匹配

record有两种模式匹配的方法:

1> rd(person, {a,b}).person2> case #person{a=10} of #person{a=A} -> A; _ -> false end.103> A.104> #person{a=B} = #person{a=15}.#person{a = 15,b = undefined}5> B.15
现在,顺道讨论下 record 模式匹配的本质。
1> rd(person, {a,b}).person2> #person{} =:= #person{a=1}.false3> case #person{a=1} of #person{} -> true; _ -> false end.true4> #person{} = #person{a=1}.#person{a = 1,b = undefined}
前面说到 record在运行期会编译成tuple,所以第2点是判断两个tuple是否相同。

而第3点和第4点本质差别不大,只是执行了模式匹配,检查tuple是否 3 个元素,且第一个元素是原子person,不会判断除其他元素值是否相等,但是如果像下面这样写就会匹配到其他元素了。

5> case #person{a=1} of #person{a=3} -> true; _ -> false end.false6> #person{a=3} = #person{a=1}.** exception error: no match of right hand side value #person{a = 1,b = undefined}

有兴趣的同学参照上面打印erlang模块汇编码就可以找到答案了。


更新说明:
2014/10/30 补充了record函数 record_info/2的说明
2014/11/06 补充了record函数is_record/2等基本操作
2015/2/11  补充了两个tuple结构修改函数
2015/3/4  补充了record的模式匹配

参考:http://blog.csdn.net/mycwq/article/details/31421341


2 0