Erlang实战练习(四)
通过前几次的练习实践相信大家对Erlang编程有了基本的认识和了解,本文通过二分搜索、echo server、进程环三个实战练习认识Erlang中进程的通信的基础,通过本次实战,主要是感受Erlang创建进程、发送消息、接受消息的过程,我们知道,Erlang并不是共享内存的通信,Erlang中节点与节点、进程与进程之间的通信都需要进行消息传递,这个我已经在前几次讨论过了。
好了,废话少说,言归正传吧。
练习1:二分搜索:从一个已排好序的列表中寻找是否包含某元素,返回true/false。
这个跟前面几次练习一样,温故列表操作,代码如下:
-module(bisearch). -export([bi_search/2,getMiddle/1,value/2,len/1]). len([]) -> 0; len([H|T]) -> 1 + len(T). head([]) -> null; head([H|T]) -> H. value(0,L) -> if L =:= [] -> %%io:format("hedad=false~n"), false; L =/= []-> %%io:format("head=~p~n",[head(L)]), head(L) end; value(N,[H|T]) -> value(N-1,T). getMiddle([H]) -> H; getMiddle(L) -> Loc = len(L) div 2, value(Loc,L).
%% 函数bi_search/2:给定一个列表,查找X所在的位置。 bi_search(L,X) -> Middle = getMiddle(L), if Middle =:= false -> false; Middle =:= X -> true; Middle < X -> Left = [E||E <- L,E > Middle], bi_search(Left,X); Middle > X -> Right = [E||E <- L,E < Middle], bi_search(Right,X) end.
由于代码是很久以前写的,很多地方都是自己编的,没有使用系统模块中的函数功能,因此,看起来可能比较繁琐,例如求列表长度、求列表表头等系统都有自定义的函数。get_middle/1主要是求出列表的中间元素。
练习2:
代码如下:
-module(echo). -export([start/0,loop/1,print/1,stop/0]). start() -> register(eserver,spawn_link(fun() -> loop([])end)). loop(X) -> receive stop -> io:format("Server has stopped!~nSee you!~n"), void; Term -> io:format("Server Received:~p~n",[Term]), loop(X) end. print(Term) -> eserver ! Term. %%向进程发送Term消息,发送原语不是send,而是感叹号!
stop() -> eserver ! stop.
spawn_link是Erlang系统的一个内建BIF(Built In Function),它是用于创建一个进程,并将当前进程与创建进程链接起来。
register也是Erlang系统的一个BIF,它是只将一个原子与一个Pid关联起来,本代码中原子eserver与新创建的进程关联起来。
程序编译运行如下:
练习3:进程环:创建N个进程,这N个进程组成一个进程环,然后将Message沿着环传送M次。
问题中提示,可以采用两种方法:一为集中式控制,二为分布式控制,我采用的集中式控制,先使用createProcess/2创建N-1个进程(当前进程为控制进程,也进行消息传递工作),然后从第2个进程开始,使用sendMessage/4将消息Message传递M次,其它的函数应该很简单,读者自行去体验,代码如下:
-module(ring0). -export([start/3,genProcess/2,createProcess/2,loop/1,sendMessage/4,sleep/1,sendDie/1]). start(M,N,Message) -> Max = N, createProcess(N,Max), %% Process1 send M number of messages to next M process sendMessage(2,M,Message,Max), sleep(1000), io:format("~nRing transfer success!~n"). createProcess(N,Max) -> L = for(N,fun(N) -> genProcess(N,Max)end ). for(1,F) -> [F(1)]; for(I,F) -> [F(I)|for(I-1,F)]. genProcess(N,Max) -> ProN = genAtom(N), register(ProN,spawn(fun()-> loop(Max) end)), io:format("process [~p] (~p) was created success!~n",[ProN,whereis(ProN)]). loop(Max) -> receive die -> quit; {From, {X,Y,Z}} -> io:format("Message (~p) From-To: (~p) to (~p)~n",[Z,From,self()]), if Y =:= 0 -> %quit; io:format("i am here~n"); Y =/= 0 -> ProM = genAtom(X), sendMessage(X,Y-1,Z,Max) %sleep(3000), %ProM ! {self(),{X,Y,Z}} end, loop(Max) end. %%向Process N 发送一条消息,此消息需要经过M步 sendMessage(N,M,Message,Max) -> if N =:= 1 -> Pid1 = whereis(genAtom(Max)), Pid2 = whereis(genAtom(N)); N =/= 1 -> Pid1 = whereis(genAtom(N-1)), Pid2 = whereis(genAtom(N)) end, %%io:format("pid1= (~p) pid2 =(~p)~n",[Pid1,Pid2]), if M =:= 0 -> quit; M =/= 0 -> %% io:format("---Message (~p) From-To: (~p) to (~p)~n",[Message,Pid1,Pid2]), if N =:= Max -> io:format("Pass 1~n"), Pid2 ! {Pid1, {1, M,Message}}; %sendMessage(1, M-1, Message,Max), N =/= Max -> io:format("Pass 2~n"), Pid2 ! {Pid1, {N+1, M,Message}} %sendMessage(N+1,M-1,Message,Max) end end. genAtom(N) -> Temp = lists:concat([process,N]), Ret = list_to_atom(Temp). sendDie(N) -> ProN = genAtom(N), ProN ! die, if N =:= 1 -> quit; N =/= 1 -> sendDie(N-1) end. sleep(T) -> receive after T -> true end.
本节主要通过2个进程创建于消息传递的例子,对Erlang系统消息传递机制进行了实战的讲解分析,通过代码分析看来,Erlang消息传递过程比较简单,无非是发送与接收的通信。真应了那句:“周瑜打黄盖——一个愿打,一个愿挨”,消息传递也是这样,只有你给我发消息的时候我才能知道你对我有意思。