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

1. Erlang 优势

  1. 并发和分布式
    • 主流语言使用共享内存模型,类似于 x = x + n 的代码导致了在多核环境下需要小心的处理锁的问题
    • Erlang 使用消息模型,Process 间不共享数据,从而避免了锁的问题
    • 无锁避免了顺序瓶颈,添加节点到网络更容易
  2. 错误处理
    • 多数语言默认认为程序不会出错
    • Erlang 采用不同的设计决策——注定要出错,那就让他出错,出错后恢复就行了。即 Erlang 程序出错后,会交由更高级的 Process 来处理(重启 Child Process、系列全部终止、重启相关 Process 等等),从而实现对错误的分级和容错处理
    • 同时带来了热更新的好处,进一步保证了可用性

2. 入门

  • Shell

    • f(). 会释放所有绑定的变量
    • 崩溃文件分析
      webtool:start().
  • 原子

    • 使用单引号括起来的字符也是原子

    • 这使得原子可以以大写字母开头,或者带有空格

      'a' = a.  %a
      'Monday'.
      'an atom with spaces'.
      
  • 列表

    • 可以包含不同类型

    • 访问列表的头是高效的,所以通常函数处理也从列表头取起

    • 插入元素到列表头部是高效的

      A = ["a","b"].
      C = ["c","d" | A ]. % ["c","d","a","b"]
      
      • 尽量避免使用 List ++ [H] 这样的操作,通常情况下添加元素到列表尾部是极为低效的(重新生成新的列表),只有在列表非常短的时候可以这样用
      • 添加在头部然后使用 lists:reverse/1 反转通常比添加在列表尾部效率要高
      • 尽量使用经过高度优化的 BIF ,比如反转列表 lists:reverse/1,可以从源码中找到它的定义,但是这个定义通常是作为简单的声明,实际上编译器会使用这个函数在系统内部更为高效的版本
  • 字符串

    • 严格说来 Erlang 中并没有字符串

    • 字符串实际上是整数列表的一种「速记/代表」形式

    • 可用 $ 来表示字符串的整数值

      A = $a.  % 97
      

3. 顺序型编程

  • beam
    beam 是 Bogdan’s Erlang Abstract Machine 的缩写

  • 匿名函数 fun

    • fun 也可以拥有多个子句

      TempFun = fun ({square, X}) -> X*X;
      	          ({double,X}) -> X+X
        	  end.
      
  • 列表解析

    • 列表解析实现最简单的 Map

      map(F, L) = [F(X) || X <- L].
      
    • 列表解析中的生成器实际上也可以起到过滤的作用

      [ X || {X, _} <- [ a,{c, d}, "aa", {"c", e}]].
      % [ c, "c"]
      
    • 快排算法

      qsort([]) ->
          [];
      qsort([Pivot | T]) ->
      			qsort([X || X <- T, X < Pivot])
      			++ [H] ++
      			qsort([X || X <- T, X >= Pivot]).
      
    • 毕达哥拉斯三元组

      pythag(N) ->
      	[{A, B, C} || A <- lists:seq(1, N),
      			  B <- lists:seq(1, N),
      			  C <- lists:seq(1, N),
      			  A + B + C =< N,
      			  A*A + B*B =:= C*C
      	].
      
    • 全排列

      perms([]) ->
      	[[]];
      perms(List) ->
      	[ [H | T] || H <- L, T <- perms(L -- [H]) ].
      
  • 断言

    • 以 when 开头

    • ; 隔开的断言,只要有一个为 true 则断言序列成立

    • , 隔开的断言,只有全部为 true 断言才会成立

    • 合法的断言必须保证没有副作用,可以包含一些无副作用的 BIF ,但无法使用用户自定义的布尔表达式函数

    • and, orandalso, orelse
      Erlang Guard: and/andalso, or/orelse

    • ===:=/==/=

      • =:= 代表精确等于,在比较的时候不会对数据类型进行转换

      • =/= 代表精确不等于

      • 99% 的情况下应使用 =:==/=

      • 模式匹配中实际上是 =:=,即 f(12) 不会匹配到 f(12.0)

        1 == 1.0. % true
        1 =:= 1.0 %false
        
    • 比较运算符的优先级

      • 不同数据类型也可以比较大小,其优先级是:

        number < atom < reference < fun < port < pid < tuple < list < binary

        <<"1">> > ["2"].   % true
        ["2"] > {"3"}. % true
        {"3"} > 'atom'. % true
        atom > 1. % true
        
  • record

    • Record 实际上只是 tuple 的「伪装」,其本质上是一个 tuple
  • case/if

    • Erlang 中不存在多个 case 匹配一个执行块的语法
    • Erlang 中任何表达式都是有值的,包括 case,因此可以有 X = case ... end
    • Case 最后使用 _ , if 最后使用 true 是保证所有分支得到匹配的方法
  • 累加器

    • 存储迭代过程中的临时容器