Erlang的静态代码检查

Dialyzer是Erlang的静态代码检查程序, 可以发现函数调用时的类型错误之类的问题, 以及被调用的函数不存在或未导出的问题. 最实用的是在上传代码到SVN后, 检查是否漏传模块. 因为漏传的话, 编译不会报错, 但运行时会出undef错误, 从SVN上检出一份全新的代码, 用Dialyzer查一下, 漏的马上可以发现.

Dialyzer用法很简单, 只有三步: 建立PLT文件, 编译代码, 运行检查.

建立PLT文件

PLT文件是Dialyzer所用的代码规则的记录, 或者说是索引. 一般把代码所用到的几个常用的Erlang系统库加到PLT文件中就好了.

开始使用Dialyzer之前请先确定Erlang已正确安装, dialyzer.exe在系统搜索路径中.

写Dialyzer模块的人用的是Linux, 所以这东西对Windows不是特别友好, 它的PLT文件默认存放在HOME目录下, 而Windows一般是没有这个环境变量的, 所以Dialyzer一运行就报错. 因此首先要设置HOME变量, 指定为一个目录, 用以存放PLT文件.

建立PLT文件的命令是

    dialyzer --build_plt --apps erts kernel stdlib mnesia

其实 --build_plt 参数表示建立PLT文件, --app 后面写上代码中引用到的内置库即可.

这个命令运行时间比较长, 可能达到十几分钟, 请去喝杯咖啡慢慢等.

如果发现已经建立的PLT文件中漏了某些库, 可以使用 --add_to_plt …

more ...

Erlang中的长度

值得吐槽的是居然连求长度这么基础的方法,Erlang都没有统一起来。 分为len和size两大对立阵营:

len阵营

  • 列表(字符串是列表的一种)的长度用length/1string:len/1。 别看后面那个打着string开头,对于不是字符串的列表也照用不误, 从源代码看其实就是前一个的马甲。
  • queue模块是len阵营的,使用queue:len/1

size阵营

  • 二进制串的长度用size/1,或是用byte_size/1bit_size/1也行,分别是 比特长度和位长度,1byte = 8bit。
  • 元组的长度也用size/1,等于其元素的个数。元组的比特长度和位长度不可用。
  • dictsetsorddictordsetsarray, gb_trees, gb_sets 这几个模块也是size阵营的,使用其各自模块 …
more ...

Erlang中的原子、字符串和二进制串

Erlang中的原子(Atom), 字符串(String)和二进制串(Binary)是有点容易让人糊涂 的东西, 这里归纳一下它们之间的区别与联系。

原子

原子是由小写字母开头, 后接大小写字母及数字及下划线的, 比方说abc, cDE, ff89, 或是由单引号''括起来的任意字符, 比方说'EXIT', '!@#@!$sdaf'。 用过其他语言的话, 会觉得Erlang中没括起来的原子长得像变量, 括起来的 长得像字符串, 但其实都不是, 这点要特别注意。Erlang中的变量是以大写字母 开头的, 字符串是用双引号括起来的。

原子在Erlang中应用非常广, 模块名、函数名、记录名都是原子, 所以如果你够蛋疼的话, 可以用'!#@$#y3fdsa1'(包括两边的单引号)当做函数名。

原子顾名思义是“不可分割”的东西, 原子无论长短, 在内存中占用的空间都是一样的, 然而, 原子的名字也不是毫无意义的, 可以利用atom_to_binary把原子名转成二进制串, 也可以用atom_to_list把原子名转成字符串。 反过来也可以 …

more ...

gen_server笔记

gen_server是erlang的OTP框架中最常用的“行为模式”了吧,至少几本erlang教材都是首 先介绍这个。

这东西用来做什么的呢?或者说,为什么要用这东西呢?由于我接触这东西不过几天, 理解尚非常粗浅,就我看来,用gen_server有以下几个好处:

  • 面向对像,封装数据与方法。gen_server内部需要维护一个状态State,并提供各种函数 给别人调用,就类似于其他语言中的“类”一样,把数据和方法封装在一起,防止数据被非法 改动。

  • 简化调用,屏蔽通讯。erlang中如果要自己实现远程过程调用(RPC)的话,需要自己 定义消息格式,自己编写封装与解包的代码,还要处理各种异常问题,这些gen_server 都帮我们做好了,只需要像平常使用函数一样直接调就用行了,省时省力还不出错。

  • 热代码替换等高级功能。 热代码替换是erlang大力宣传的一项特色功能,不停机维护 在生产上是非常美妙的事。不过这是高级功能,初学暂时用不上,书上也没多讲。

gen_server模板

gen_server是可以使用模板来写的,如下:

    -module().

    -behaviour(gen_server).

    %% API …
more ...

初识Erlang,Hello World(4)

前几节的Hello world都是自言自语,今天试试对着别人说。首先是在本机上各个进程之间, 然后是在网络上不同机器之间。

进程间的消息:单向接收

Erlang里可以很方便地创建进程,这种进程是超轻量级的,运行于erlang虚拟机内部的, 而与操作系统的进程管理无关的(也就是说你在任务管理器中只会见到一个Erlang进程), Erlang内的进程创建开销很小,创建一个进程所需的时间仅为微秒级,内存消耗也很少, 一台机上运行数以千计的进程也没问题。

进程之间使用消息进行通讯。示例如下:

    hw15srv() ->
        receive
            {name, Name} ->
                io:format("Hello ~p ~n", [Name]),
                hw15srv()
        end.

    hw15() ->
        Pid = spawn(fun hw15srv/0),
        Pid ! {name, "alice"},
        Pid ! {name, "Bob"}.

hw15/0首先是使用spawn命令启动了一个hw15srv/0进程 …

more ...

初识Erlang,Hello World(3)

今天不只要对World说Hello,还要对别人说Hello

    hw(Who) ->
        io:format("Hello ~p ~n", [Who]).

平平无奇的一个函数,对Who说Hello

如果要对一组人说Hello,可以这么做:

    hw10(L) ->
        [hw(X) || X <- L].

    hw10() ->
        L = ["ali", "bob", "cat"],
        hw10(L).

上面是用了列表解析的方法。

也可以用lists:map,效果和上面是一样的

    hw11(L) ->
        lists:map(fun hw/1, L).

    hw11() ->
        L = ["ali", "bob", "cat"],
        hw11(L).

另一个很相似的做法是lists:foreach:

    hw14(L …
more ...