且听疯吟 如此生活三十年
Erlang Programming 笔记 2

4. 异常

  • 抛出异常

    • 显式的 exit(Why)
      • 终止当前 Process
      • 如果当前 Process 未捕获这个异常,则系统会向所有 link 该 Process 的 Process 广播 {'EXIT', Pid, Why} 消息
    • 显式的 throw(Why)
      • 抛出供其调用者捕获的异常
      • 最好是添加注释说明抛出的异常
      • 如何处理由调用者选择,包括忽略
    • 系统错误 erlang:error(Why)
  • try..catch 捕获异常

    • 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 bit

          Red = 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
            决定字节序,默认 big
          • Sign = signerd / unsigned
            仅用于模式匹配,默认 unsigned
          • Type = integer/ float / binary
            默认 integer
          • Unit = 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 默认编码为 ISO-8859-1 (Latin-1)
      • Erlang 内部没有字符串数据类型,而是用一串整数列表表示
      • 因此要注意 Unicode 字符的处理
      • 使用 unicode 模块中如 unicode:characters_to_list 的函数处理 unicode 字符
    • 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}].
        ...
        
    • 引用
      使用 BIF erlang:make_ref() 创建引用