4. 异常
抛出异常
- 显式的
exit(Why)
- 终止当前 Process
- 如果当前 Process 未捕获这个异常,则系统会向所有 link 该 Process 的 Process 广播
{'EXIT', Pid, Why}
消息
- 显式的
throw(Why)
- 抛出供其调用者捕获的异常
- 最好是添加注释说明抛出的异常
- 如何处理由调用者选择,包括忽略
- 系统错误
erlang:error(Why)
- 显式的
try..catch
捕获异常try/catch 本身会消耗一点性能
%% 首先对 FuncOrExpressionSequence 求值 %% 如果没有产生异常则顺序进行 Patterm 匹配, 匹配成功后执行后面的表达式 %% 如果有异常抛出, 则顺序匹配 ExPattern(ExceptionType 是 throw、exit、error 中的一个, 默认为 throw) %% after 块中的代码用于清理工作,绝对会执行 %% after 可以省略 try FuncOrExpressionSequence of Pattern1 [when Guard1] ->Expressions1; Pattern2 [when Guard2] ->Expressions2; ... catch ExceptionType: ExPattern1 [when ExGuard1] ->ExExpressions1; ExceptionType: ExPattern2 [when ExGuard2] ->ExExpressions2; ... after AfterExpressions end.
栈跟踪
在触发异常的时候可以调用
erlang:get_stacktrace/0
来查看最近的栈跟踪信息,可以获得异常函数的调用路径(尾递归调用除外)try func() catch error: X -> {X, erlang:get_stacktrace()} end.
5. 顺序型编程进阶
BIF (built-in function)
binary
相对于 List 活着 tuple , binary 更节省内存,输入输出更为高效
binary_to_term/term_to_binary
可将任何的 binary 和 Erlang 值(List、atom、tuple 甚至 binary 等)转换成为 binary,但是这个 binary 是以所谓的 「外部数据格式」 存储的,类似于其他语言的序列化与反序列化)A = <<1,2,3>>. B = term_to_binary(A). %% <<131,109,0,0,0,3,1,2,3>>
binary 中的数字,每一个都在 0~255 之间(因为每一个数字表示一个 byte,其值在 0000 0000 ~ 1111 1111 [0 ~ 29 -1] 之间)
GB2312 的汉字转成 UTF-8 时,绝大多数是 3 个字节
A = <<"且听疯吟"/utf8>>. %% <<228,184,148,229,144,172,231,150,175,229,144,159>>
bit 语法
封包与解包
以 RGB 色彩为例:
创建一个 16 bit (2 byte)的内存块,存放一个 RGB 三元组,并为 Red 分配 5 bit,为 Green 分配 6 bit,为 Blue 分配 5 bitRed = 2, Green = 60, Blue = 29, RGB = <<Red:5, Green:6, Blue:5>>. %% <<23, 157>> <<R1:5, G1:6, B1:5>> = RGB. {R1, G1, B1}. %% {2, 60, 29}
注意取值范围。比如我们为 Blue 分配了 5 bit,但是为 Blue 赋值 61,转换成二进制为 111101,超过了 5 bit,则会发生截断,取低五位,即实际值为 29
integer_to_list(61,2). %% "111101" integer_to_list(29,2). %% "11101" <<2:5,60:6,61:5>> == <<2:5,60:6,29:5>>. %% true
- bit 语法表达式
- 形如
<<E1, E2, E3, ... , En>>
- 每一个元素都是二进制数据中的一个区块
- 元素的值表示有下面几种方式
Value
Value:Size
Value/TypeSpecifierList
Value:Size/TypeSpecifierList
- 其中
TypeSpecifierList
是形如End-Sign-Type-Unit
组成的字符串,其中不同的 Type 选项如下,每一个都可以忽略且没有顺序要求:End = big / native / little
决定字节序,默认 bigSign = signerd / unsigned
仅用于模式匹配,默认 unsignedType = integer/ float / binary
默认 integerUnit = 1 / 2 / 3 / ... / 255
- 整个区块长度为 Size * Unit,其值必须大于或等于 0 且是 8 的倍数
- 其值依赖于 Type,如果 Type 是 integer 或者 float,则值为 1 ,是 binary 则值为 8
- 形如
- binary 模式匹配
apply
apply(Module, Func, [Arg1, Arg2, ... , Argn])
- 对于参数个数已知的函数,
M:Func([Arg1, Arg2, ... , Argn])
的调用优于 apply - 使用 apply 调用的函数编译器无法优化,同时许多分析工具也无法分析其细节
- 如无必要尽量少用 apply
属性
- module
- import
- export
- compile
- 编译器选项,常用的
-compile(export_all).
- 编译器选项,常用的
- vsn
无语法意义,用于文档分析 - 用户定义属性
-SomeTag(Value)
- 该值会被编译进模块
- 该值可以在运行时使用
attrs:module_info()
提取 beam_lib
提供了一系列不加载模块代码就能分析的函数
块表达式
把多个表达式组织为单个表达式begin Expression1, Expression2, ... ExpressionN end
布尔
- Erlang 中不存在布尔类型,通常使用原子
true/false
代替作为布尔符号使用 - 布尔表达式:
and / not / or / xor
- Erlang 中不存在布尔类型,通常使用原子
字符集
- Erlang 默认编码为
ISO-8859-1 (Latin-1)
- Erlang 内部没有字符串数据类型,而是用一串整数列表表示
- 因此要注意 Unicode 字符的处理
- 使用 unicode 模块中如
unicode:characters_to_list
的函数处理 unicode 字符
- Erlang 默认编码为
epp
- Erlang 预处理器,扩展宏,插入必须的包含文件等
转义字符
表达式/表达式序列
- 表达式序列的值为序列中最后一个表达式的值
函数引用
包含文件
include
include_lib
列表操作符
++
- 列表添加
ListA ++ ListB
即把 ListB 附加到 ListA 尾部 - 模式匹配中的
++
f("begin" ++ T) -> ...
即相当于模式匹配[$b, $e, $g, $i, $n | T]
- 列表添加
--
列表删除ListA -- ListN
,即从 ListA 中删除 ListB 中的所有元素,若元素 E 在 B 中重复出现 k 次,则只会从 ListA 中按顺序删除 k 个 E
宏
- 定义
- 不带参数
-define(Macro, ...)
- 带参数
-define(Macro(X,Y), ...)
- 预定义宏
?FILE
当前文件名?MODULE
当前模块名?LINE
当前行号
- 不带参数
- 宏的流程控制
-undef(Macro)
取消宏定义,在此语句后无法使用该宏ifdef / ifndef / else / end
- 结合调试标志,扩展 debug 日志输出等
- 定义
数值类型
- 整型
- 整型的计算是精确的
- 其计算结果代表的数据长度只受限于可用内存
- 表示法
- 传统表示法,
1, 1989, -1337
- K 进制整数,
2#01001011,16#fe34
- 传统表示法,
- 浮点型
- 内部以 IEEE 754 的 64 bit 格式表示
- 整型
操作符优先级
进程字典
每一个 Erlang Process 都有自己的进程字典
实际上提供的功能类似于全局变量
由于不是非破坏性赋值,会导致代码有副作用
尽量少用
添加/获取进程字典
@spec put(Key, Value) -> OldValue | undefined. @spec get(Key) -> Value | undefined. @spec get() -> [{Key, Value}]. ...
引用
使用 BIFerlang:make_ref()
创建引用