开篇
恩,被赤裸裸的打脸了,刚说完每周2更,结果拖到现在都没有一更。。恩。。虽然中间本人完成了人生大事的一部分--订婚。
好了,废话不多说,今天来介绍下 异常,内置函数,二进制数据和所对应的比特语法,以及如何编译和运行程序等等。
异常
- 简介
关于异常,其实在传统的编程语言里面使用的比较多,诸如:Java,C#。恩,C和C++是个例外。在erlang中,异常捕获的语法也大同小异.
try ... of
Pattern [when guard] -> expression;
...other pattern...
catch
exception type : Pattern [when guard] -> express;
...other pattern...
after
expression;
...other expression...
end.
- 异常捕获
其中,try ... of 中是需要捕获异常的表达式或者函数。函数或者表达式的值,会逐步与 of 下面的模式进行匹配,并给出匹配到的表达式的值。
在catch中,捕获异常类型。erlang定义的异常类型只有三种:
throw-> 调用者处理的可忽略的异常,
exit-> 程序退出类型异常,
erlang:error -> 程序崩溃类型异常
Final Handling
after处的表达式不论异常是否捕获,均会处理。根据传统语言的编程经验来看,此处比较适合处理资源释放,如:数据库连接释放,文件释放等等。-
demo
光说不练假把势,写一个捕获所有可能产生异常的函数,以及写一个产生所有异常的函数进行测试:
可以先忽略test2函数,稍后会说到。generate_exception/1 函数会产生一个函数被调用时所有可能出现的结果,catcher/1 函数尝试对这些结果进行异常捕获或者正常处理。test/0 函数将这些结果合并到一个列表中。
另外一种写法
当然,捕获异常还有另外一种写法:
catch expression.
直接捕获表达式中的异常.如 demo 代码中的 test2/0 函数.
- 编程规范
捕获异常还是有几种best practise的。如:
1. 经常会返回错误的程序
case ... of
{ok, Val} -> do_something(Val);
{error, Why} -> %% do something with error message Why
end.
2. 出错几率小的代码
try ...
catch
throw:{thisError, Val} -> ...
exit:{otherError, Val} ->...
end.
3. 捕获所有可能的异常
try expression
catch
_:_ -> ... %% 切记,此处不能写成 _ -> 。如此则捕获不到任何异常.
end.
BIF
BIF 即 Built in functions,也就是内置函数,或称之为系统函数更为合适些。erlang中的BIF看起来像是函数,其实不然,他是针对于erlang虚拟机的操作指令。如:
@spec time() -> 获取当前时间
@spec lists:reverse(List) -> List. 翻转列表
@spec term_to_binary(Term) -> binary(). 将所有erlang的值转换为二进制数据.n
注: @spec 为erlang中的文档标记用法
比特语法
简介
比特语法主要用于处理二进制数据,经常用于对于单个比特位或者比特位串进行封包或者解包。Examples
%%解析COFF数据
-define(DWORD, 32/unsigned-little-integer).
-define(LONG, 32/unsigned-little-integer).
-define(WORD, 16/unsigned-little-integer).
-define(BYTE, 8/unsigned-little-integer).
<<Characteristics : ?DWORD,
TimeDateStamp : ?DWORD,
MajorVersion : ?WORD,
MinVersion : ?WORD,
NumberOfNamedEntries : ?WORD,
NumberOfIdEntries : ?WORD>>.
上述代码参照COFF的结构体进行二进制数据读取,可见,尤为简单。无需移位,截断等等。
编译运行
运行的方式大致有三种,shell中运行,单文件编译运行,工程编译运行。
- 运行环境设置
erlang虚拟机自动加载模块时,首先要指定模块文件所在的路径,两种方式,可以在启动时,通过-pa, -pz
指定添加。pa 为加载搜索路径的头,pz 为加载搜索路径的末尾。也可以在 目录下的.erlang文件中,通过2个BIF添加@spec code:add_patha(Dir)
,@spec code:add_pathz(Dir)
前者加入到搜索路径的头,后者加入到搜索路径的末尾。如:
io:format("Allen@erlang virtual machine~n").
code:add_pathz(".");
运行结果:
shell运行
shell运行时,如果需要退出,最佳方案是使用 q(). 方式进行退出,而不是erlang:halt(). 如果使用 erlang:halt(). 会使得部分程序(数据写入等)产生错误修复,再下一次启动时.
若shell不允许输入时,只能通过杀掉进程的方式停止运行。注意,如果erlang程序是以 -heart Cmd 的方式启动,启动时会带上监控进程,监控进程在进程死亡时会执行Cmd,若Cmd的指令为重启程序,那么首先要杀掉这个监控进程。单文件编译运行
可以在shell中,使用c(filename).
进行编译,然后使用模块对应的函数。如:
-module (hello).
-export([hi/0]).
-auth(allen).
-date("2015.08.28").
-vsn("1.0.0").
hi () ->
"Hello World".
执行方式:
也可以在命令行下编译并执行,如:
erlc hello.erl
erl -noshell -s hello hi -s init stop
结果:
当然,还有一种方式则是编写 escript 或者 编写sh脚本运行程序。(使用 -eval 命令)--> 限于篇幅,将在后续IM工程中使用时介绍。
- makefile
赞叹下,makefile不愧为工程神器。从cpp,到erlang,到ELisp,到...。真是太赞了。
所以,erlang的makefile写法与cpp的也没啥区别。
下面列出一个简要的模板:
.SUFFIXES: .erl .beam
.erl.beam:
erlc -W $<
ERL = erl -boot start_clean
MODS=hello
all: compile
${ERL} -pa "." -s hello hi
compile: ${MODS:%=%.beam}
clean:
rm -rf *.beam erl_crash.dump
执行结果如下:
- rebar
当然,编写makefile也有点麻烦,业界比较好用的工具有个rebar。rebar不单单可以进行工程的编译,还可以创建工程模块,以及发布等等。非常强大。
此处不做赘述,请移步Github