erlang 数据传递

学习完erlang的基本语法,发觉这门语言的语法还算简单,要掌握的东西并不多,有几点感悟:模式匹配实在是太好用了,无全局变量不能修改变量这两条似乎处处制约着编写程序。

Talk is cheap,show me the code

简单的双人聊天室

这里需要三个角色:user1,user2,server。

user需要2个操作:send,recv。

程序分析

1
2
1. user1发出消息,那么系统将其收入待处理的队列中。
2. 随后user2接收了消息,remain队列中的数据同时交给user2和history队列,remain变空。

难点

1
2
1. remain、history队列在每次请求之后都会变化。
2. 没有全局变量,该如何取得2个队列

解决

1
其实第一个问题的答案不言而喻,erlang只能使用函数来修改数据。所以代码应该长成这样
1
2
3
handle(S)->
NewS...
handle(NewS).
1
2
这个参数S从在修改的过程中随处可见,不正是所需要的"全局变量"?
只要将修改、获取的操作都放在handle里,这样一来二个问题都解决了。

但是这又引入了新的问题,handle不能空转,它需要user给它指示。
erlang自带的消息功能将派上大用场。

1
2
3
4
5
6
7
Send:
pid| registered name | {Name,Node} ! message

receive
Pattern1 [when GuardSeq1] -> Body1
[after Timeout->Body2]
end

每一个进程都有自己的信箱,只有匹配的信件才会被取出。利用信箱来完成消息的同步,就能实现以聊天功能了。顺便模仿一下gen_server的写法。其主要特点有:1.使用behaviour模式,使得灵活地创建各类模块;2.用一个结构化变量来存储所需的数据。

下面给出完整代码:

s_gen_server.erl:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
%%%-------------------------------------------------------------------
%%% @author PC
%%% @copyright (C) 2016, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 15. 涓冩湀 2016 10:04
%%%-------------------------------------------------------------------
-module(s_gen_server).
-author("PC").
%% API
-export([call/2, terminate/1, main/1]).
%% behaviour
-export([start_link/2, behaviour_info/1, handle/1, init/2]).
%%-spec init(Arg)->{ok,state}|{error,Reason}.
%%-spec handle_call(Request,From,State) -> {ok,new_state}|{error,Reason}.
%%-spec start_link(Module, Args)->Result.
%%Result={ok,Pid}.
behaviour_info(callbacks) ->
[{init, 1},
{handle_call, 3}];
behaviour_info(_Other) ->
undefined.
handle(State) ->
receive
{Module, From, Request} ->
case Module:handle_call(Request, From, State) of
{_Type, _State} -> From ! {_Type, _State}, handle(_State)
end;
stop ->
receive
Bagger -> io:format("~p~n", [Bagger])
after
1 -> finish
end
end.
%%API
start_link(Module, Args) ->
Pid=spawn(?MODULE, init, [Module, Args]),
register(Module,Pid).
init(Module, Args) ->
{ok, State} = Module:init(Args),
handle(State).
call(Module, Request) ->
Module ! {Module, self(), Request},
receive
{noreply, _State} -> ok;
{reply, State} -> State
end.
terminate(Module) ->
Module ! stop.
main(_T) ->
start_link(chat, "chat_center").

chat_server.erl:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
%%%-------------------------------------------------------------------
%%% @author PC
%%% @copyright (C) 2016, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 15. 涓冩湀 2016 10:23
%%%-------------------------------------------------------------------
-module(chat_server).
-author("PC").
-behaviour(s_gen_server).
%% history=[Msg::{who,string()}]
-record(state, {history = [], remain = [], servername}).
-export([init/1, handle_call/3]).
%% API
-export([start_link/0, recv/0, send/2,stop/0,show_history/0]).
start_link() ->
s_gen_server:start_link(?MODULE, "chat_center").
send(Who, Word) ->
s_gen_server:call(?MODULE, {send, Who, Word}).
show_history() ->
State = s_gen_server:call(?MODULE, fetch),
State#state.history.
stop() ->
s_gen_server:terminate(?MODULE).
-spec recv() -> #state{} .
recv() ->
State = s_gen_server:call(?MODULE, fetch),
s_gen_server:call(?MODULE, {update, State#state{remain = [], history = State#state.history ++ [State#state.remain]}}),
State#state.remain.
%% behaviour
init(_Arg) ->
{ok, #state{servername = _Arg}}.
handle_call({send, Who, String}, _From, State) ->
{noreply, #state{remain = State#state.remain ++ [{Who, String}]}};
handle_call(fetch, _From, State) ->
{reply, State};
handle_call({update, NewState}, _From, _) ->
{noreply, NewState}.

坚持原创技术分享,您的支持是我前进的动力!