Erlang入门(一)

读erlang.org上面的Erlang Course四天教程
1.数字类型,需要注意两点
1)B#Val表示以B进制存储的数字Val,比如

7> 2#101.
5

二进制存储的101就是10进制的5了
2)$Char表示字符Char的ascii编码,比如$A表示65

2.比较难以翻译的概念——atom,可以理解成常量,它可以包含任何字符,以小写字母开头,如果不是以小写字母开头或者是字母之外的符号,需要用单引号包括起来,比如abc,‘AB‘

3.另一个概念——Tuple,有人翻译成元组,可以理解成定长数组,是Erlang的基础数据结构之一:

8> {1,2,3,4,5}.
{1,2,3,4,5}
9> {a,b,c,1,2}.
{a,b,c,1,2}
10> size({1,2,3,a,b,c}).
6

内置函数size求长度,元组可以嵌套元组或者其他结构。下面所讲的列表也一样。

4.另外一个基础数据结构就是各个语言都有的list(列表),在[]内以,隔开,可以动态改变大小,

[123, xyz]
    [123, def, abc]
    [{person, ‘Joe‘, ‘Armstrong‘},
        {person, ‘Robert‘, ‘Virding‘},
        {person, ‘Mike‘, ‘Williams‘}
    ]

可以使用内置函数length求列表大小。以""包含的ascii字母代表一个列表,里面的元素就是这些字母的ascii值,比如"abc"表示列表[97,98,99]。

5.通过这两个数据结构可以组合成各种复杂结构,与Lisp的cons、list演化出各种结构一样的奇妙,Erlang也可以当作是操作列表的语言。

6.Erlang中变量有两个特点:
1)变量必须以大写字母或者下划线开头,可以包含字母、下划线和@
2)变量只能绑定一次,也就是所谓的Single Assignment。或者以一般的说法就是只能赋值一次,其实Erlang并没有赋值这样的概念,=号也是用于验证匹配。

7.模式匹配——Pattern Matching,Erlang的模式匹配非常强大,看了buaawhl的《Erlang语法提要》的介绍,模式匹配的功能不仅仅在课程中介绍的数据结构的拆解,在程序的分派也扮演重要角色,或者说Erlang的控制的流转是通过模式匹配来实现的。具体功能参见链接,给出书中拆解列表的例子:

[A,B|C] = [1,2,3,4,5,6,7]
        Succeeds - binds A = 1, B = 2,
        C = [3,4,5,6,7]
    
    [H|T] = [1,2,3,4]
        Succeeds - binds H = 1, T = [2,3,4]
    
    [H|T] = [abc]
        Succeeds - binds H = abc, T = []
    
    [H|T] = []
        Fails

下面会给出更多模式匹配的例子,给出一个模块用来计算列表等

8.Erlang中函数的定义必须在一个模块内(Module),并且模块和函数的名称都必须是atom,函数的参数可以是任何的Erlang类型或者数据结构,函数要被调用需要从模块中导出,函数调用的形式类似:
moduleName:funcName(Arg1,Arg2,...).
写我们的第一个Erlang程序,人见人爱的Hello World:

-module(helloWorld).
-export([run/1]).
run(Name)->
    io:format("Hello World ~w~n",[Name]).

存为helloWorld.erl,在Erlang Shell中执行:

2> c(helloWorld).
{ok,helloWorld}
3> helloWorld:run(dennis).
Hello World dennis
ok

打印出来了,现在解释下程序构造,

-module(helloWorld).

这一行声明了模块helloWorld,函数必须定义在模块内,并且模块名称必须与源文件名相同。

-export([run/1]).

而这一行声明导出的函数,run/1指的是有一个参数的run函数,因为Erlang允许定义同名的有不同参数的多个函数,通过指定/1来说明要导出的是哪个函数。
接下来就是函数定义了:

run(Name)->
    io:format("Hello World ~w~n",[Name]).

大写开头的是变量Name,调用io模块的format方法输出,~w可以理解成占位符,将被实际Name取代,~n就是换行了。注意,函数定义完了要以句号.结束。然后执行c(helloWorld).编译源代码,执行:

helloWorld:run(dennis);

9.内置的常用函数:

date()
    time()
    length([1,2,3,4,5])
    size({a,b,c})
    atom_to_list(an_atom)
    list_to_tuple([1,2,3,4])
    integer_to_list(2234)
    tuple_to_list({})
    hd([1,2,3,4])  %输出1,也就是列表的head,类似Lisp的car
    tl([1,2,3,4])  %输出[2,3,4],也就是列表的tail,类似List的cdr

10.常见Shell命令:
1)h(). 用来打印最近的20条历史命令
2)b(). 查看所有绑定的变量
3) f(). 取消(遗忘)所有绑定的变量。
4) f(Val).  取消指定的绑定变量
5) e(n).   执行第n条历史命令
6) e(-1).  执行上一条shell命令

11.又一个不知道怎么翻译的概念——Guard。翻译成约束?呵呵。用于限制变量的类型和范围,比如:

number(X)    - X 是数字
    integer(X)    - X 是整数
    float(X)    - X 是浮点数
    atom(X)        - X 是一个atom
    tuple(X)    - X 是一个元组
    list(X)        - X 是一个列表
    
    length(X) == 3    - X 是一个长度为3的列表
    size(X) == 2    - X 是一个长度为2的元组
    
    X > Y + Z    - X >Y+Z
    X == Y        - X 与Y相等
    X =:= Y        - X 全等于Y
    (比如: 1 == 1.0 成功
               1 =:= 1.0 失败)

为了方便比较,Erlang规定如下的比较顺序:

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

其中pid就是process的id。

12.忘了介绍apply函数,这个函数对于熟悉javascript的人来说很亲切,javascript实现mixin就得靠它,它的调用方式如下:

apply(Mod, Func, Args),三个参数分别是模块、函数以及参数列表,比如调用我们的第一个Erlang程序:

apply(helloWorld,run,[dennis]).

13.if和case语句,if语句的结构如下:if   Guard1 ->        Sequence1 ;   Guard2 ->        Sequence2 ;...end而case语句的结构如下:

case Expr of
Pattern1 [when Guard1] -> Seq1;
Pattern2 [when Guard2] -> Seq2;

PatternN [when GuardN] -> SeqN
end

if和case语句都有一个问题,就是当没有模式匹配或者Grard都是false的时候会导致error,这个问题case可以增加一个类似java中default的:

case Fn of

   _ ->
   true
end

通过_指代任意的Expr,返回true,而if可以这样:

if
  
  true ->
   true
end

一样的道理。case语句另一个需要注意的问题就是变量范围,每个case分支中定义的变量都将默认导出case语句,也就是在case语句结束后可以被引用,因此一个规则就是每个case分支定义的变量应该一致,不然算是非法的,编译器会给出警告,比如:

f(X) ->
case g(X) of
true -> A = h(X), B = A + 7;
false -> B = 6
end,
h(A).

如果执行true分支,变量A和变量B都被定义,而如果执行的false分支,只有变量B被定义,可在case语句执行后,h(A)调用了变量A,这是不安全的,因为变量A完全可能没有被定义,编译器将给出警告
variable ‘A‘ unsafe in ‘case‘ (line 10)

14.给出一些稍微复杂的模型匹配例子,比如用于计算数字列表的和、平均值、长度、查找某元素是否在列表中,我们把这个模块定义为list:

-module(list).
-export([average/1,sum/1,len/1,double/1,member/2]).
average(X)->sum(X)/len(X).
sum([H|T]) when number(H)->H+sum(T);
sum([])->0.
len([_|T])->1+len(T);
len([])->0.
double([H|T]) -> [2*H|double(T)];
double([]) -> [].
member(H, [H|_]) -> true;
member(H, [_|T]) -> member(H, T);
member(_, []) -> false.

细细体会,利用递归来实现,比较有趣,这其实与Lisp中利用尾递归来实现迭代是一样的道理,[H|T]的形式类似Lisp中的car、cdr操作。_用于指代任意的变量,当我们只关注此处有变量,但并不关心变量的值的时候使用。用分号;来说明是同一个函数定义,只是不同的定义分支,通过模式匹配来决定调用哪个函数定义分支。
另一个例子,计算各种图形的面积,也是课程中给出的例子:

-module(mathStuff).
-export([factorial/1,area/1]).
factorial(0)->1;
factorial(N) when N>0->N*factorial(N-1).
%计算正方形面积,参数元组的第一个匹配square    
area({square, Side}) ->
    Side * Side;
%计算圆的面积,匹配circle  
area({circle, Radius}) ->
   % almost :-)
   3 * Radius * Radius;
%计算三角形的面积,利用海伦公式,匹配triangle 
area({triangle, A, B, C}) ->
   S = (A + B + C)/2,
math:sqrt(S*(S-A)*(S-B)*(S-C));
%其他
area(Other) ->
   {invalid_object, Other}.

执行一下看看:

1> c(mathStuff).
{ok,mathStuff}
2> mathStuff:area({square,2}).
4
3> mathStuff:area({circle,2}).
12
4> mathStuff:area({triangle,2,3,4}).
2.90474
5> mathStuff:area({other,2,3,4}).
{invalid_object,{other,2,3,4}}

Erlang使用%开始单行注释。

from:http://www.blogjava.net/killme2008/archive/2007/06/13/123860.html

时间: 2024-10-18 19:28:57

Erlang入门(一)的相关文章

Erlang入门:构建application练习5(监督树)

1.前言 在<Erlang入门:构建application练习4(进程link的作用)>一文中, 提到了一个问题: 如果系统进程内部发生了错误而异常退出,将会出现什么情况? 在application中,像bank_center这样的系统进程,如果终止了,整个系统就无法正常运行,为了让bank_center意外终止时能自动重启,这里就要请Erlang的监督树出来帮忙了. 演示项目bank3目录结构: 2.编写监督树回调模块bank_sup.erl 监督树的回调模块只有一个函数init/1 %%

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 基本都是些用于

erlang 入门笔记

1.下载安装erlang,我使用的是windows 64 版本的,直接安装,设置环境变量%ERL_HOME%\bin (设置环境变量只是使用方便而已,可设置也可不设置) 2.从cmd 中 启动erlang命令行,如果没有设置环境变量,则需要cd到erlang/bin中 C:\Users\Administrator>erl Eshell V6.4 (abort with ^G) 1> 3.编写helloworld.erl (erlang源文件后缀为.erl) %%erlang中 标识代码结束使用

Erlang入门:构建application练习4(进程link的作用)

1.示例项目(bank)简介 bank_server.erl是为客户服务的进程回调模块 bank_center.erl是为银行服务中心的进程回调模块 客户(bank_server)每年都要扣除年费 所有年费都交给银行服务中心(bank_center) 客户服务使用示例: 创建用户:bank_server:create_account(myname, 100). 用户存钱:myname ! {deposit, 10}. 用户取钱:myname ! {cash1, 20}. myname ! {ca

Erlang入门:进程实例操作与练习1

%% @author Rolong<rolong at vip dot qq dot com> -module(process1). -compile(export_all). -define(I(F), io:format(F++"~n", [])). -define(I(F, A), io:format(F++"~n", A)). %% Usage: %% %% 启动进程:Pid = process1:start(Fun). %% Fun = fun

Erlang入门:进程实例操作与练习2

%% @author Rolong<[email protected]> -module(process2). -compile(export_all). -include("common.hrl"). %% Usage: %% 启动服务:process2:start(). %% 查看时间:myserver ! now. %% 结束服务:myserver ! stop. start()-> Pid = spawn(?MODULE, server, []), %% 给服

Erlang入门:构建application练习1

1.定义application回调模块 %% 文件名为bank_app.erl -module(bank_app). %% 指定behaviour为application -behaviour(application). %% 导出application回调模块的函数 -export([start/2, stop/1]). %% 启动application时调用 start(_Type, _Args) -> ("Start ~p ...", [?MODULE]), {ok, se

Erlang入门:服务进程实例操作与练习1

这是一个用Erlang进程实现的简单银行服务 使用示例如下: 创建用户:bank:create_account(myname, 100). 用户存钱:myname ! {deposit, 10}. 用户取钱:myname ! {cash1, 20}. myname ! {cash2, 30}. 查询余额:myname ! check. 创建用户:bank:create_account(rolong, 100). 用户存钱:rolong ! {deposit, 10}. 用户取钱:rolong !

Erlang入门:gen_server实例操作与练习2

Eshell V5.10.4 (abort with ^G) 1> bank_server3:create_account(name1, 1000). true 2> name1 ! check. Current money is: 1000 check 3> erlang:send(name1, check). Current money is: 1000 check 4> erlang:send(name1, abcd). handle_info: abcd abcd Year

Erlang入门(三)——分布式编程

明天要回家一个星期了,好好休息下.今天找到别人翻译的Erlang编程手册,值的好好读一遍.    所谓分布式的Erlang应用是运行在一系列Erlang节点组成的网络之上.这样的系统的性质与单一节点上的Erlang系统并没有什么不同.分布式这是个“大词”,Erlang从语言原生角度支持分布式编程,相比于java简单不少.一.分布式机制下列的BIFs是用于分布式编程:spawn(Node, Mod, Func, Args)启动远程节点的一个进程 spawn_link(Node, Mod, Func