模式匹配
=表示一个模式匹配操作。Lhs=Rhs实际上是这样一个过程,对右端求值(Rhs),然后将结果与左端(Lhs)进行模式匹配。
变量
绑定变量:一个指针,指向存放那个值的存储区,而那个值是无法改变的。
运算符
除法:
/ 保留小数
div 整除
rem 求余
原子
用来表示不同的非数字常量值
erlang中的原子时全局有效的,而且无需使用宏定义或者包含文件。
原子是一串以小写字母开头,后跟数字字母或下划线或邮件符号的字符。
使用单引号引起来的字符也是原子。
元组
一定数量的项组成单一的实体。
将若干个以逗号分割的值用一对花括号括起来,形成元组。
通常可以使用一个原子作为元组的第一个元素来标明这个元组所代表的含义。
实例:
Person={
person,
{name,jack},
{age,18},
{sex,boy}
}.
声明元组时,就自动创建了元组,不再使用它们时,元组也随之销毁。erlang使用垃圾搜集器去收回没有使用的内存,因此我们不用担心内存分配的问题。
如果创建的一个元组引用了一个已绑定的变量,那么新元组将会享有这个变量所引用的数据结构。
实例:
F={firstName,joe}.
L={lastName,tom}.
P={F,L}.
列表
用列表存储数目可变的东西。将若干个以逗号分割的值用一对方括号括起来,就形成了一个列表。
列表的第一个元素称为列表的头,剩下的东西称为列表的尾。列表的尾通常还是一个列表。
访问列表的投射一个非常高效的操作,因此,实际上所有列表处理函数都是从提取列表头开始的,先对头进行处理,然后继续处理列表的尾。
定义列表
如果T是一个列表,那么[H|T]也是一个列表。这个列表以H为头,以T为尾。竖线符号可以将列表的头和尾分隔开来,而[]则是空列表。
字符串
严格地讲,erlang中并没有字符串,字符串实际上就是一个整数列表。用双引号('')将一串字符括起来就是一个字符串。
Name="Hello".
这里的"Hello"仅仅是一个速记形式,实际上它意味着一个整数列表。列表中的每一个元素都有相应的字符的整数值。[ASCII码]
shell打印一串列表值时,只有列表中的所有整数都是可打印字符,它才把这个列表当作字符串来打印。
可以使用$符号来表示字符的整数值。例如,$a实际上是一个整数,表示字符a。
f() 会让shell释放它绑定过的所有变量。执行这个命令后,所有的变量都变成自由变量。
模块
模块是erlang中代码的基本单元,我们编写的所有函数都存于模块之中。模块文件通常存放在以.erl为扩展名的文件中。
要运行一个模块,首先需要编译它,编译成功之后模块文件其扩展名是.beam。
geometry.erl
-module(geometry).
-export([area/1]).
area({rectangle,Width,Ht}) ->Width*Ht;
area({circle,R}) ->3.14159*R*R.
编译:
c(geometry).
//编译并加载
调用:
geometry:area({rectangle,10,5}).
子句的顺序并不重要,无论这些子句的顺序如何,对于程序来说,运行的效果都是一样的。因为各子句的模式彼此互不相干。这使编写和扩展程序变得很简单,只须添加新的模式就行了。不过,通常来说子句的顺序还是有点关系的,因为进入一个函数的时候,调用是按照模式在文件中的顺序依次进行匹配的。
注意:
1.area函数由若干个不同的子句构成。当调用这个函数时候,对其调用参数的匹配过程从第一个子句开始依次向下进行。
2.函数不能处理模式匹配失败的情形,此时程序会失败并抛出一个运行时错误。这一点是故意为之。
area函数友两个子句构成,子句间以分号分隔,最后一条子句的后面以句点作为结束符。每一个子句都有一个函数头和一个函数体,函数头由函数名合 随后的以括号括起来的模式组成,函数体则由一系列表达式组成,如果函数头中的模式与调用参数匹配成功的话,其对应的表达式就会进行运算。模式将按照它们出现在函数定义中的先后顺序进行匹配。
购物清单
shop.erl
-module(shop).
-export([cost/1]).
cost(oranges) ->5;
cost(newspaper) ->8;
cost(apples) ->2;
函数cost/1由5个子句组成,每个子句的头部都包括了一个模式(本例之中的模式非常简单,只是一个原子而已)。当我们对shop:cost(X) 求值时,系统会用X对这些子句的每一个模式进行匹配。如果匹配了某个模式,那么紧跟在->之后的代码就会执行。
cost/1函数必须从模块之中导出,如果你想从模块的外部调用它,这是必须的。
cost(pears) ->9;
cost(milk) ->7.
shop1.erl
-module(shop1).
-export([total/1]).
total([{What,N}|T]) ->shop:cost(What)*N+total(T);
total([]) ->0.
erlang中的标点符号:
逗号(,)用来分隔函数调用、数据构造器以及模式中的参数。
句号(.)(后跟一个空白符号) 用来在shell中分隔完整的函数和表达式。
分号(;) 用来分隔子句。
同名不同目的函数
函数的目(atity)就是它所拥有的参数数量。在erlang中,同一个模块中的两个函数,如果它们同名但是目并不相同,这样的两个函数被认为是完全不相同的。它们之间除了名字蹊跷相同之外,彼此之间再无其它关联。
fun
匿名函数。
Z=fun(X) ->5*X end.
TempConvert=fun({c,C}) ->{f,32+C*9/5};
({f,F}) ->{c,(f-32)*5/9}
end.
erlang是一种函数式的编程语言。fun既可以作为函数的参数,也可以作为函数(或者fun)的结果。
以fun为参数的函数
list是标准库中的一个模块,从中导出的很多函数都是以fun作为参数的。其中,最有用的是lists:map(F,L)。
Dobule=fun(X) ->2*X end.
L1=[1,2,3,4].
lists:map(Double,L1).
另一个常用的函数是lists:filter(P,L),它返回一个新列表,新列表由列表L中每一个能满足P(E)为true的元素组成。
Even=fun(X) ->(X rem 2) =:=0 end.
lists:filter(Even,L1).
map和filter这样在一个函数调用中处理整个列表的操作称为list-at-a-time操作。
返回fun的函数
Fruit=[apple,orange,pear].
MakeTest=fun(L) ->(fun(X) ->lists:member(X,L) end) end.
IsFruit=MakeTest(Fruit).
//这个测试函数将会检查它所传入的参数是否为列表L中的成员。
Test=fun(L) ->2*L end.
erl文件
-module(模块名 必须跟文件名一致).
-export([函数名/参数个数]). //声明可以在外部调用
-import(模块名,[函数名/参数]).
shop2.erl
-module(shop2).
-export([total/1]).
-import(lists,[sum/1,map/2]).
total(L)->
sum(
map(fun({What,X})->shop:cost(What)*X end,L
)
).
调用
shop2:total([{apples,7},{milk,5}]).
列表解析
列表中每一项的值乘以2:
L1=[1,2,3,4].
[2*X||X<-L1].
T1=fun(L) ->[2*X||X <-L] end.
简洁的map代码:
t4.erl
-module(t4).
-export([map\2]).
map(F,L) ->
[F(X) ]
[X || Qualifier1,Qualifier2,...]
X可以是任意一个表达式,每个限定词(qualifier)可以是一个生成器或者是一个过滤器。
*生成器通常写为Pattern<-ListExpr,其中ListEpr必须是一个对列表项求值的表达式。
*过滤器是一个谓词(返回true或者false的函数),也可以使一个布尔表达式。
快速排序
-module(t8).
-export([qsort/1]).
qsort([]) ->[];
qsort([Pivot|T]) ->
qsort([X||X<-T,X
++[Pivot]++
qsort([X||X<-T,X>=Pivot]).
运算符
断言
断言是一种用于强化模式匹配功能的结构。使用断言,可以在一个模式上做一些简单的变量测试和比较。求两数大小值max函数
-module(e1).
-export([max/2]).
max(X,Y) when X>Y ->X;
max(X,Y) ->Y.
当X大于Y的时候,第一个子句得到匹配,其结果为X。
如果第一个子句不匹配,那么尝试匹配第二个子句,第二个子句总是返回第二个参数Y。此时Y必定大于等于X,否则,第一个子句会先被匹配。
函数定义的头部使用断言,此时断言必须以关键字when开头,说当然也可以在任何语言中允许使用表达式的地方使用断言。当断言用于表达式时候,
它们会被求值为一个原子true或者false。如果断言求值为true则求值成功,否则求值失败。
断言序列
断言序列可以是单个断言也可以是一系列用分号(;) 分开的断言集合。在断言集合中G1;G2;G3;...只要任何一个断言为true,那么整个断言序列就为true。
断言也可以是逗号分割的断言集合。断言集合G1,G2 当所有的断言都为true,整个断言序列才为true。
合法的断言表达式形式:
1.原子true
2.其他常量(条件或者绑定变量) 都会求值为false
3.断言谓语
is_atom(X) X是原子
is_binary(X) X是二进制数据
is_constant(X) X是常数
is_float(X) X是浮点数
is_function(X) X是函数
is_function(X,Y) X是有N个参数的函数
is_integer(X) X是整数
is_list(X) X是列表
is_number(X) X是数值
is_pid(X) X是进程标示符
is_port(X) X是端口
is_reference(X) X是引用
is_tuple(X) X是元组
is_record(X,Tag) X是标记为Tag的记录
is_record(X,Tag,N) X是标记为Tag大小为N的记录
断言BIF
abs(X) X的绝对值
element(N,X) 元组X的第N个元素
float(X) 将数字X转换为浮点数
hd(X) 列表X的头部
length(X) 列表X的长度
node() 当前节点
round(X) 将数字X转换为整数(四舍五入)
self() 当前进程的进程标示符
size(X) X的大小,X为元组或二进制数据
trunc(X) 将数字X转换为整数(截取)
t1(X) 列表的尾部
断言示例:
is_tuple(T),size(T) =:=6,abs(element(3,T))>5
element(4,X) =:=hd(L)
X=:=dog;X=:=cat
is_integer(X),X>Y;abs(Y) <23
布尔表达式
orelse和andalso 操作符的原因是布尔操作符and/or 的原始定义中需要对它们的参数都求值。
X=0
f(X) when (X==0) or (1/X>2) -> //false
g(X) when (X==0) orelse (1/X>2) ->//true
记录:把一个名称与元组中的一个元素对应起来。
定义纪录并保存在.hrl为扩展名的文件中
record.hrl
-record(todo,{status=reminder,who=jow,text}).
读取纪录: rr(read record) rr("records.hrl").
X=#todo{}. //创建一个新纪录
X1=#todo{status=urgent,text="Fix errata in book"}. //创建一个新纪录
语法#todo{k1=v1,...} 用于创建类型为todo的新纪录 key都是原子,而且必须和纪录定义中的一样。若省略key,那么就使用纪录定义中的默认值。
X2=X1#todo{status=done}. //复制一个存在的记录。语法X1#todo{status=done}表示创建一个X1的副本(X1必须为todo类型),并将value字段的值修改为done。只是原纪录的一个副本,原纪录本身并没有改变。
从纪录中提取字段值
使用模式匹配。
#todo{who=W,text=Txt} =X2.
匹配操作符的左边,我们使用了自由变量W和Txt来定义一个纪录模式。如果匹配成功,这些变量将会绑定到纪录中对应的字段值。
点语法
X2#todo.status.
在函数中队纪录进行模式匹配
clear_status(#todo{status=S,who=W}=R)->
R#todo{status=finished}
为匹配一个特定类型纪录,可以这样来定义函数:
do_something(X) when is_record(X,todo) ->ok.
纪录只是元组的伪装
纪录就是元组。
释放纪录的定义rf()
-module(b2).
-export([filter/2]).
-export([filter1/4]).
filter(F,[H|T]) ->filter1(F(H),F,H,T);
filter(F,[]) ->[].
filter1(true,F,H,T) ->[H|filter(F,T)];
filter1(false,F,H,T) ->filter(F,T).
case表达式
语法
case Expression of
Pattern1 [when Guard1] ->Expr_seq1;
pattern2 [when Guard2] ->Expr_seq2;
end
执行过程:
首先对Experssion进行求值(嘉定最后的结果保存为value),然后,value依次对Pattern1(带有可选的断言Guard1)、Pattern2等进行模式匹配,直到找到一个可以匹配的分支。一旦找到这样的分支,就对其后相应的表达式序列进行求值--这个表达式序列的求值结果页就是case表达式的返回值。如果没有任何分支被匹配到,那么就会抛出一个异常。
if表达式
语法
if
Guard1 ->
Expr_seq1;
Guard2 ->
Expr_seq2;
end.
try..catch表达式是有值的
BIF 内建函数
tuple_to_list({5,6,7,9,10}). //把元组转换成列表
time(). //获得当前时间的时分秒。
二进制数据
二进制数据以一个整数或者字符序列的形式出现,两端分别用两个小于号和两个大于号括起来。
二进制数据中用到的整数,每一个都必须要在0到255之间。由字符序列组成的二进制数据等同于由其每一个字符的ASCII编码组成的二进制数据,即<<"cat">>等同于<<99,97,116>>s
<<5,10,20>>.
如果二进制数据是可打印 的字符串,则输出该字符串
<<99,99>>.
@spec func(Arg1,..,Arg) ->Val
erlang类型文档标记。在erlang社区中,常用这个标记在文档中描述函数的参数以及其返回类型。
term_to_binary(数值) 转换成二进制数据
binary_to_term(二进制数据) 还原原始的数据项
size(二进制数据) 返回二进制数据长度
比特语法
//封装一个16bit字长的内存区域,也就是变量M中。X要在结果中占3bit,Y占7bit,Z占6bit
M=<>
16bit色彩的封包和解包
红色通道分配5bit
绿色通道分配6bit
蓝色通道分配5bit
创建16 bit大小的内存块Men 用来存放RGB三元组
Red=2.
Green=61.
Blue=20.
Mem = <>.
数据的提取
<> = Mem.
比特语法表达式:
<<>> <>
Ei=Value |
Value:Size|
Value/TypeSpecifierList |
Value:Size/TypeSpecifierList
二进制数据中的总比特数必须要恰好能被8正处
创建一个二进制数据时候,Value必须是一个绑定变量,文本串或者一个返回值为整数,浮点数或者二进制数据的表达式。而在比特语法用来做模式匹配操作时候,Value则既可以是绑定 的变量,也可以是自由的变量,其类型可以为整数、文本串、浮点数或者二进制数据
apply
apply(mod,func,[arg1,arg2]) 将mod模块中的func函数应用到参数arg1,arg2上去
mod:funx(arg1,arg2).
例子:
apply(erlang,atom_to_list,[hello]).
属性
-AtomTag(..) 模块属性 定义文件的属性。 模块属性氛围:预定义属性和用户定义属性。
-module(modname).
模块声明。modname必须是一个原子。
-import(Mod,[Fun1/Num1,Fun2/Num2]).
从Mod模块中分别导入Fun1 Fun2 本模块调用的时候不需要特别声明模块名
-export([fun1/num1,fun2/num2])
从模块中导出 fun1 fun2 被到处的函数才能在模块外部调用。
用户定义属性
-Tag(V)
Tag必须是一个原子,V必须是一个文字项。模块属性的值被编译金模块并可以再运行时候被提取出来。
查看模块信息
mod:module_info(). 返回一个与编译模块相关的所有元数据的属性列表。
mod:module_info(attributes). 返回与文件相关的特定属性的列表。
上面两个函数是每次模块在编译时候自动创建。
块表达式
begin
Expr1,
..
ExprN
end.
可以使用快表达式来把一串表达式组成一个类似子句的实体。块表达式的值就是块中最后一个表达式的值。
当代码的某一处的语法只允许使用单个表达式而你却又需要在这个地方使用一串表达式时,就可以使用块表达式。
布尔类型 true false
布尔表达式 not 逻辑非 and 逻辑与 or 逻辑或 xor 逻辑异或
字符集 erlang源代码文件默认以ISO-8859-1字符集进行编码。
erlang内部没有字符数据类型。字符串不是一个真正的类型,而是用一串整数列表来表示。
注释 %
表达式和表达式序列
任何可以被求出值的东西都被称作表达式。
表达式序列式一系列由逗号分开的表达式。这些表达式都应该进阶放置于箭头(->之后)。表达式序列的值被定义Wie序列中最后一个表达式的值。
函数引用
fun LocaFunc/Arity
double(L) ->lists:map(fun d4:square/1,L).
包含文件
-include_lib(filename).
可以包含扩展名为.hrl的包含文件。filename应该包含一个绝对或者相对路径以便预处理器能够定位到相应的文件。
列表操作符++和-- (中缀操作符)
A++B 以为着把A和B加起来(实际上市B附加到A)
A--B 从列表A中删除列表B。B中的所有元素都要从A中删除。
模式匹配之中的++
f("begin" ++ T) -> 扩展为($b,$e,$g,$i,$n|T)。
宏
-define(macrol(X,Y),{a,X,Y}).
foo(A) ->
?macrol(A+10,b).
宏扩展之后成为:
foo(A) ->
{a,A+10,b}.
预定义宏
?FILE扩展为当前文件名。
?MODULE扩展为当前模块名。
?LINE扩展为当前行号。
-undef(Macro) 取消宏定义
-ifdef(Macro) 只有Macro被定义后,才对该行以下的代码进行运算。
-else 只能在ifdef或者ifndef之后出现。如果条件为false,那么该语句后的代码才会被执行。
-endif 标记ifdef或者ifndef语句的结束
条件宏必须是合理嵌套,它们通常这样出现
数值类型
整型和浮点型
整型:
1.传统语法
2.K进制整数 二进制2#001 十六进制 16#af 最高进制为36进制。
3.$语法 $C表示ASCII字符C的整数值 $a 97
浮点型
一个浮点数有5个部分:一个可选符号位,一个数值部分,一个小数点,一个小数部分以及一个可选的指数部分。
进程字典
erlang的每一个进程都有自己的私有数据存储,叫做进行字典。进程字典是由一系列键值对组成的关联数组,一个键对应一个值
put(K,V) 向进程字典假如一个k,v键值对。以前关联过返回以前关联过的值,没有则返回原子undefined,
get(k) 查找k对应的值,没有则返回原子undefined
get_keys(v) 返回字典中值为v的键的列表
erase(k) 返回对应的k的值并删除该值
引用
引用时全局唯一的erlang值,使用BIF erlang:make_ref()来创建引用。
短路布尔表达式
Expr1 orelse Expr2 Expr1 andalso Expr2
比较表达式
> < =< >= == /=不等于 =:=全等于 =/=全不等于
number < atom < reference < fun < port < pid
下划线变量
_v 如果一个变量在一个子句中只被使用一次,那么编译器通常都会提出警告,因为这很有可能是存在错误的征兆。如果变量仅被使用一次,但以下划线开始,那么编译器就不会产生警告信息。
停止erlang shell
erlang:halt().