Erlang入门(五)——补遗
暂时搞不到《Programming Erlang》,最近就一直在看Erlang自带的例子和Reference Manual。基础语法方面有一些过去遗漏或者没有注意的,断断续续仅记于此。
1。Erlang的保留字有:
after and andalso band begin bnot bor bsl bsr bxor case catch cond div end fun if let not of or orelse query receive rem try when xor
基本都是些用于逻辑运算、位运算以及特殊表达式的符号
2.Erlang的类型,除了在前面入门一提到的类型外,还包括:
1)Binary,用于表示某段未知类型的内存区域
比如:
1> <<10,20>>.
<<10,20>>
2> <<"ABC">>.
<<65,66,67>>
2)Reference,通过调用mk_ref/0产生的运行时的unique term
3)String,字符串,Erlang中的字符串用双引号包括起来,其实也是list。编译时期,两个邻近的字符串将被连接起来,比如"string" "42" 等价于 "string42"
4)Record,记录类型,与c语言中的struct类似,模块可以通过-record属性声明,比如:
-module(person).
-export([new/2]).
-record(person, {name, age}).
new(Name, Age) ->
#person{name=Name, age=Age}.
1> person:new(dennis, 44).
{person,dennis,44}
在编译后其实已经被转化为tuple。可以通过Name#person.name来访问Name Record的name属性。
3.模块的预定义属性:-module(Module).
声明模块名称,必须与文件名相同-export(Functions).
指定向外界导出的函数列表-import(Module,Functions).
引入函数,引入的函数可以被当作本地定义的函数使用-compile(Options).
设置编译选项,比如export_all
-vsn(Vsn). 模块版本,设置了此项,可以通过beam_lib:version/1
获取此项信息
可以通过-include和-include_lib来包含文件,两者的区别是include-lib不能通过绝对路径查找文件,而是在你当前Erlang的lib目录进行查找。
4.try表达式,try表达式可以与catch结合使用,比如:
try Expr
catch
throw:Term -> Term;
exit:Reason -> {'EXIT',Reason}
error:Reason -> {'EXIT',{Reason,erlang:get_stacktrace()}}
end
不仅如此,try还可以与after结合使用,类似java中的try..finally,用于进行清除作用,比如:
termize_file(Name) -> {ok,F} = file:open(Name, [read,binary]), try {ok,Bin} = file:read(F, 1024*1024), binary_to_term(Bin) after file:close(F) end.
5.列表推断(List Comprehensions),函数式语言特性之一,Erlang中的语法类似:
[Expr || Qualifier1,...,QualifierN] Expr可以是任意的表达式,而Qualifier是generator或者filter。还是各举例子说明下。
1> [X*2 || X <- [1,2,3]]. [2,4,6]
2> L=[1,2,3,4,5,6,7].
[1,2,3,4,5,6,7]
3> [X|X<-L,X>=3].
[3,4,5,6,7]
再看几个比较酷的例子,来自Programming Erlang,
比如快速排序:
-module(qsort).
-export([qsort/1]).
qsort([])->[];
qsort([Pivot|T])->
qsort([X||X<-T,X<Pivot])
++ [Pivot] ++
qsort([X||X<-T,X>=Pivot]).
搜索勾股数组:
%勾股数组
-module(pythag).
-export([pythag/1]).
pythag(N)->
L=lists:seq(1,N),
Square=fun(X) when is_number(X)->X*X end,
[{A,B,C}||
A<-L,
B<-L,
C<-L,
A+B+C=<N,
Square(A)+Square(B)=:=Square(C)].
列表推断将大大减少你的敲击键盘的次数。
6.宏,定义常量或者函数等等,语法如下:
-define(Const, Replacement). -define(Func(Var1,...,VarN), Replacement).
使用的时候在宏名前加个问号?,比如?Const,Replacement将插入宏出现的位置。系统预定义了一些宏:?MODULE 表示当前模块名
?FILE 当前模块的文件名
?MODULE_STRING 同上,但是以字符串形式
?LINE 调用的当前代码行数
?MACHINE 机器名
Erlang的宏与C语言的宏很相似,同样有宏指示符,包括:
-undef(Macro).
- 取消宏定义
-ifdef(Macro).
- 当宏Macro有定义的时候,执行以下代码
-ifndef(Macro).
- 同上,反之
-else.
- 接在ifdef或者ifndef之后,表示不满足前者条件时执行以下代码
-endif.
- if终止符
假设宏-define(Square(X),X*X).用于计算平方,那么??X将返回X表达式的字符串形式,类似C语言中#arg
一个简单的宏例子:
-ifdef(debug).
-define(LOG(X), io:format("{~p,~p}: ~p~n", [?MODULE,?LINE,X])).
-else.
-define(LOG(X), true).
-endif.
-define(Square(X),X*X).
-compile(export_all).
test()->
A=3,
?LOG(A),
B=?Square(A),
io:format("square(~w) is ~w~n",[A,B]).
当编译时不开启debug选项的时候:
17> c(macros_demo).
{ok,macros_demo}
18> macros_demo:test().
square(3) is 9
当编译时开启debug之后:
19> c(macros_demo,{d,debug}).
{ok,macros_demo}
20> macros_demo:test().
{macros_demo,11}: 3
square(3) is 9
ok
可以看到LOG的输出了,行数、模块名以及参数
7、Process Dictionary,每个进程都有自己的process dictionary,用于存储这个进程内的全局变量,可以通过下列
BIFs操作:
put(Key, Value)
get(Key)
get()
get_keys(Value)
erase(Key)
erase()
8、关于分布式编程,需要补充的几点
1)节点之间的连接默认是transitive,也就是当节点A连接了节点B,节点B连接了节点C,那么节点A也与节点C互相连接
可以通过启动节点时指定参数-connect_all false来取消默认行为
2)隐藏节点,某些情况下,你希望连接一个节点而不去连接其他节点,你可以通过在节点启动时指定-hidden选项
来启动一个hidden node。在此情况下,通过nodes()查看所有连接的节点将不会出现隐藏的节点,想看到隐藏的节点
可以通过nodes(hidden)或者nodes(connected)来查看。
完整的erl选项如下:
-connect_all false |
上面已经解释。 |
-hidden |
启动一个hidden node |
-name Name |
启动一个系统成为节点,使用long name. |
-setcookie Cookie |
与Erlang:set_cookie(node(), Cookie) .相同,设置magic cookie |
-sname Name |
启动一个Erlang系统作为节点,使用short name |
注意,short name启动的节点是无法与long name节点通信的。
9.一个小细节,在Erlang中小于等于是用=<表示,而不是一般语言中的<=语法,我犯过错误的地方,同样,不等于都是用/号,而不是
!,比如/=、=/=。
10.and or 和andalso orelse的区别
and和or会计算两边的表达式,而andalso和orelse的求值采用短路机制,比如exp1 andalso exp2,当exp1返回false之后,就不会去求值
exp2,而是直接返回false,而exp1 and exp2会对exp1和exp2都进行求值,or与orelse也类似。
今天在erlang-china下到了《Programming Erlang》,准备打印一份看看,进入OTP的学习。