第5章 顺序型编程进阶
摘自:http://hi.baidu.com/zai215837829/blog/item/7520d139b98fda3996ddd871.html
1. 所有的BIF都在erlang模块中,大部分常用的BIF都已被自动导入(也就是说还有一些不常用的没有自动导入)。
2. 二进制数据相对于元组和列表,它更加节省内存,输入输出更加高效。
3. 在书写和打印时,二进制数据用一组以<<和>>括起来的整数或字符序列的形式出现。
如:<<5,10,20>>. <<"hello">>
整数要在0-255之间,字符相当于其对应的ASCII码。
4. 可以通过BIF或二进制语法来构造或提取二进制数据。
5. 操纵二进制数据的BIF
@spec list_to_binary(IoList) -> binary()
其中IoList是一个列表,其中的元素可以是0~255的整数、二进制数据或者另外一个IoList。亦即IoList可以是一个嵌套的列表。
@spec split_binary(Bin, Pos) -> {Bin1, Bin2}
从Pos位置将Bin分成两部分。如果是从1开始的话,第Pos个元素被分在了Bin1中。
@spec term_to_binary(Term) -> Bin
可以将任何一个Erlang值转化为二进制数据。转化成的数据可以保存在文件中,通过网络发送等。(如何发送的C写的程序的话,C程序如何解析呢?)
@spec binary_to_term(Bin) -> Term
term_to_binary的逆函数。
@spec size(Bin) -> Int
返回二进制数据的长度。
6. 比特语法
<<E1, E2, ..., En>>
Ei = Value | %% Size省略时整数是8位,浮点型是64位
Value:Size | %% Size代表单元区块的长度,有可能有多个区块(Unit),整个区块的长度Size*Unit
Value/TypeSpecifierList |
Value:Size/TypeSpecifierList
TypeSpecifierList是一个由连字符(-)间隔的形如End-Sign-Type-Unit的列表。
其中@type End = big | little | native 默认值是big
@type Sign = signed | unsigned 默认值是unsigned
@type Type = integer | float | binary 默认值是integer
@Unit = 1|2|...|255 当Type是integer或float时,Unit的默认值是1; 当Type是binary,Unit的默认值是8
7. 测试本机的整数表示法:
{<<16#12345678:32/big>>, <<16#12345678:32/little>>, <<16#12345678:32/native>>, <<16#12345678:32>>}
8. 如果I是一个整数,<<I:32>>可以将I转换一个32位长的二进制数据。
9. apply(Mod, Func, [Arg1, Arg2, ..., ArgN])调用Mod模块中的Func函数,Arg1~ArgN是Func的参数。apply的好处是可以动态改变模块和函数。
10. 预定义属性:(-record()和-include()不是属性)
-module(modname). %% 模块名要与文件名一致,这个属性必须是文件的第一个属性
-import(Mod, [Name1/Arity1, Name2/Arity2, ...]). %% 从Mod模块中导入函数
-export([Name1/Arity1, Name2/Arity2, ...]). %% 从当前模块中导出函数。
-compile(Options). %% 向编译器选项列表中添加Options。Options是一个编译器选项或者一个编译器选项列
表(在compile模块手册中)
调试程序时可以用-compile(export_all).导出当前模块的所有函数。
-vsn(Version). %%Version可以是任何形式的文字项。
11. 用户定义属性语法:-SomeTag(Value). SomeTag必须是原子,Value必须是文字项。
12. Mod:module_info()返回一个与编译模块相关的所有元数据的属性列表。
Mod:module_info(attributes)则返回与文件相关的特定属性的列表。
还有Mod:module_info(exports), Mod:module_info(imports), Mod:module_info(compile)。
13. Mod:module_info/0, Mod:module_info/1是编译器在编译源文件时自动加入到模块中的函数。(我想可能在模块加载时有用。)
14. 可以用beam_lib:chunks("test.beam", [attributes])来看一下模块的属性信息。这个函数可以在没有加载模块时对模块进行分析并提取属性。
15. 块表达式:以逗号分隔一个串表达式,其值是最后一个表达式的值。在语法中需要一个表达式,而我们却要多个表达式的时候,可以用一下。
begin
Expr1,
...,
ExprN
end
16. 布尔类型:erlang中没有独立的布尔类型,通常用原子true和false来代替吧。
17. 布尔表达式:
逻辑非: not B1
逻辑与: B1 and B2
逻辑或: B1 or B2
逻辑异或: B1 xor B2
18.编译预处理:命令compile:file(M, ['p'])可以对M.erl文件进行预处理,处理完后输出M.P文件,这个文件中存入所有经过扩展的宏和所有已经手插入的包含文件。
19. 转义符:在字符串(也就是列表)或使用单引号的原子中,可以使用转义字符。
20. 在 erlang中,任何可以被求出值的东西都被称作表达式。这就意味着catch, if, try...catch等都是表达式。而类似记录和模块属性等,这些不能被求值的都不是表达式。表达式序列是一系统以逗号分隔的表达式,这些表达式应该紧 接放置在->之后,表达式序列的值是最后一个表达式的值。
21. 函数引用(与C中的函数指针相似):
方法一:fun LocalFunc/Arity
方法二:fun Mod:RemoteFunc/Arity
如:lists:map(fun square/1, L). 再如:lists:map(fun x1:square/1, L).
22. 包 含文件:-include(Filename),这个语法可以包含扩展名为.hrl的包含文件。FileName应该包含一个绝对或者相对路径。 -include_lib(name),这个语法可以引入库的包含文件,如:-include_lib("kernel/include /file.hrl").
23. 列表操作符++和--:
A++B意味着把A和B加起来,把B附加到A上生成一个新的列表。
A--B从列表A中删除列表B。删除的意思是B中的所有元素都要从A中删除。注意,如果符号X在B中出现K次,那么在A中只会按顺序删除K个X。
++也可以用在模式匹配中。如:f("begin"++T) ->...,这个模式会被扩展为[$b, $e, $g, $i, $n|T]
24. 宏:
宏定义的语法:
-define(Constant, Replacement).
-define(Func(Var1, Var2, ..., VarN), Replacement).
在代码中使用宏:?MacroName
25. 系统预定义宏:
?FILE 扩展为当前文件名
?MODULE 扩展为当前模块名
?LINE 扩展为当前行
26. 宏的流程控制
-undef(Macro). 取消宏定义,在这个语句后不能再调用这个宏
-ifdef(Macro). 只有Macro被定义后,才对该行以下的代码进行运算。
-ifndef(Macro). 只有在不定义Macro的情况下,才对该行以下的代码进行运算。
-else. 只能在ifdef或ifndef后出现
-endif. 标记if的结束。
27. c(m1, Options)命令可以在编译代码时向编译器传输一些选项,如c(test, {d, debug}).在编译test.erl时定义一上debug宏,这样在代码中的ifdef(debug)就为true了。
28. 模式匹配操作符
func1([{tag, {one, A}=Z1, B}=Z2|T}]) ->
f(..., Z2, ...),
g(..., Z1, ...),
...
其中Z1相当于{one, A}, 而Z2相当于{tag, {one, A}, B}
29. 数值类型:erlang中的数值类型包括整型和浮点型
三种表示整数的方法:
(1). 传统语法:12, -234
(2). K进制语法:K#Digits, 2#00101010, 16#af6bfa23
(3). $语法:$C代表C的ASCII码,也可以是转义字符,如:$/n.
浮点数:1.0, 3.14159, -2.3e+6, 23.56E-27
erlang中可表示的浮点数的范围是-10的323次幂~10的308次幂。
30. 操作符优先级:
:
#
(unary)+, (unary)-, bnot, not
/, *, div, re, band, and
+, -, bor, bxor, bsl, bsr, or, xor
++, --
==, /=, =<, >=, >, =:=, =/=
andalso
orelse
31. 进程字典:erlang的每一个进程都有自己的私有数据存储,叫做进程字典。进程字典是由一系列键值对组成的关联数组,像C++的map
可以使用下面的BIF来操纵进程字典:
@spec put(Key, Value) -> OldValue. %% 如果没有之前关联过的值,则返回原子undefined。
@spec get(Key) -> Value. %% 如果没有这个Key,则返回原子undefined。
@spec get() -> [{Key, Value}].
@spec get_keys(Value) -> [Key].
@spec erase(Key) -> Value. %% 如果没有这个Key,则返回原子undefined。
@spec erase() -> [{Key, Value}]
尽是避免使用进程字典,因为进程字典不是非破坏性赋值。
有一种情况可以,那就是使用进程字典来存储“一次性写入”变量。如果一个键与一个值仅绑定一次而且不再修改。
32. 引用:引用是一个全局唯一的Erlang值,可以用BIF make_ref()来创建引用。好像有点像全局唯一标识。
33. 短路布尔表达式:
Expr1 orelse Expr2 %% 如果Expr1结果为false,才会对Expr2求值
Expr1 andalso Expr2 %% 如果Expr1结果为true,才会对Expr2求值
在A or B和A and B中,无论什么情况A和B都会被求值。
34. 比较表达式
X>Y
X<Y
X=<Y %% X小于等于Y, 这个很奇怪
X>=Y
X == Y %% 用于数值类型
X /= Y %% 用于数值类型
X =:= Y %% X全等于Y
X =/= Y %% X不全等于Y
不同的类型也能比较
number<atom<reference<fun<port<pid<tuple<list<binary
35. ==和/=在比较整数和浮点数时可以对两边的数值进行类型转换。而=:=和=/=确不会。
如:1.0 == 1(true) 1.0 =:= 1(false)
另外,由此产生的一个问题是,如果我们定义了一个函数F=fun(12)->...end. 那么当我们调用F(12.0)时会出错。
36. 下划线变量:如果一个变量只出现一次,以后没有被使用过,编译时会出现警告。如果不想看到这个警告的话,可以将其命令为一个以下划线开头的变量_VarName.