《Learn You Some Erlang for Great Good!》的学习笔记(四)

前面我们都是在erlang shell下进行的编程,然而我们最终编程是希望以文件的格式来保存、运行代码,为此,我们需要了解模块。

       模块简单来说就是一系列函数的集合,erlang中所有的函数都需要定义在模块之中,你其实已经用到模块了,只是有可能你还没有注意到。前面提到过的BIFs(内建函数),如hd、tl等实际上属于erlang模块,只不过“erlang:”可以被省略。正常调用函数的语法是:Module:Function(Arguments) ,列表相关的操作都在lists模块下,而输入与输出相关的操作都位于io和file模块中。

       erlang的模块有两个要素组成:方法和属性。所有的属性定义方式为:-Name(Attribute). 而对于模块来说,必不可少的属性是:-module(Name). 这一句始终是模块的第一个属性,Name是一个原子,其它模块将使用这个名称来调用其中的函数,特别需要注意的是这个Name必须要跟文件名一致,否则将无法编译。

        我们需要熟悉的第二个属性是export属性,它用来声明哪些方法可以被其它模块调用(相当于java中的public,而未导出的方法就相当于private),语法如下所示

      -export([Function1/Arity, Function2/Arity,..., FunctionN/Arity]).

       其中arity表示函数形参的数量,比如add(X,Y)和add(X,Y,Z)可以分别作为add/2和add/3导出。

       接下来我们就来了解函数的定义,基本定义形式为:Name(Args) -> Body. Name是一个原子,而函数体可以包含多个由逗号“,”分隔的表达式。函数以句号作为结束标志。erlang没有return,因为在它看来return毫无意义,erlang函数将最后的表达式返回的值作为函数的返回值,你不需要显式的返回。

       了解了上述这些之后,你就可以实现一个简单的module啦,如下所示:


       其中需要注意的是,根据惯例,对于module的注释,采用%%%开头,而对于行首的注释使用%%,在行末进行注释的话则使用%。在idea中,你只需在相应的方法中右击即可进行编译运行。

       而对于常用的其它模块中的方法,erlang也支持import导入,基本如法为:

        -import(Module,[Function1/Arity, ..., FunctionN/Arity]).

       对于上面的例子,你只需要-import(io, [format/1]). 然后你就可以直接调用format("Hello,World!~n").然而在erlang中并不建议大家使用import,因为这样确实会降低程序的可读性,也会导致于当前模块中的重名方法造成冲突。但是一般来说,lists模块你可以导入,因为确实他的使用频率太高了。

       除了上面介绍的使用idea来编译运行之外,也可以选择手动编译运行,一般通用的方法是erlc hello.erl,如果你进入了erlang shell,也可以采用c(hello)。编译成功之后,你将会在目标目录下看到beam结尾的文件,这就是erlang的可执行文件,调用也十分简单,默认只需在erlang shell中,cd到beam的目录下,就可以直接使用module:function(Args)的方式来进行方法调用。

       在编译时,常用的选项有如下:

1. -debug_info

       所有的调试器、代码覆盖率工具和静态代码分析工具等都需要在编译时带上此选项,方能工作。

2. -export_all

       这个标志为导出所有模块内的函数,此标志主要用于测试的时候,不应该在正式发布产品时使用。

3. -{outdir,Dir}


       这个就不多说了,用来指定目标beam文件的路径,默认是放置于当前目录。

4. -{d,Macro} or {d,Macro,Value}


       这个用户在模块中添加宏定义。

示例:c(useless, [debug_info, export_all]).

宏定义:

       erlang中的宏定义和C中的相似,主要是用来定义简单函数和常量的,你也应该多使用宏定义来避免程序中出现魔术数字。在erlang中宏定义语法如下:

       -define(MACRO,some_value).

       而当你使用宏时,只需在前面加上问号即可:?MACRO。

       erlang中也有一些预先定义好的宏,如:?MODULE用来代表当前模块的名称(atom);?FILE用来代表当前文件名称;?LINE用来返回当前行号。你也可以像c一样在宏定义中加上条件定义:

-------------------------------

-ifdef(DEBUGMODE).

-define(DEBUG(S), io:format("dbg:"++S)).

-else.

-define(DEBUG(S), ok).

-endif.


-------------------------------

       对于模块的信息,你还可以使用MODULE:module_info()来进行查看,如下所示:

 

       其中vsn相当于模块的标示符(已除去注释),这个是用于热加载和一些版本管理。你也可以自己指定代码的标志符,在模块中使用-vsn(VersionNumber)即可。

发布了42 篇原创文章 · 获赞 9 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/jjxojm/article/details/50614768