wxErlang 的案例学习(1)

这些天,一直在鼓捣wxWidgets。装上了code::blocks,学了bakefile,为的是辅助使用wxErlang。
前期的铺垫告一段落,正戏开场。
一直以为,学习也要讲效率,学以致用,没用的不学。要真正做到这点,不难,集中精力学习examples就行。
examples集中了知识精华,展示了技术诀窍,是把握关键技术的捷径。
学习examples,全面深入地琢磨程序的运行机制,有助于理解掌握技术细节。
挑选的案例,是erl5.7.1/lib/wx-0.98.1/examples/sudoku。其中有4个erl模块,将挨个详细解剖分析。
一、模块sudoku.erl
-module(sudoku).
-export([go/0]).
-compile(export_all).
-include("sudoku.hrl").
start() -> 
    spawn_link(fun() -> init(halt) end).
go() -> 
    spawn_link(fun() -> init(keep) end).
init(Halt) ->
    ?TC(sudoku_gui:new(self())),
    receive {gfx, GFX} -> ok end,
    case sudoku_game:init(GFX) of
Halt -> erlang:halt();
Stop -> exit(Stop)
    end.
tc(Fun,Mod,Line) ->
    case timer:tc(erlang, apply, [Fun,[]]) of
        {_,{'EXIT',Reason}} -> exit(Reason);
        {T,R} ->
            io:format("~p:~p: Time: ~p/n", [Mod, Line, T]),
            R
    end.
(1)、编译指令-export([go/0])和-compile(export_all)
-export([go/0])是说,其他模块可以在源码级调用本模块的函数go,即:sudoku:go().
-compile(export_all)是说,本模块编译成.beam后,全部函数均开放调用。
例如,在eshell中,直接调用以下函数都是允许的:
3> sudoku:init(keep).
4> sudoku:tc(fun() -> sudoku_gui:new(self()) end, sudoku, 40). 
可是,如果屏蔽指令 %% -compile(export_all),将只能调用 sudoku:go(),否则,会出错:
10> sudoku:init(keep).                                             
** exception error: undefined function sudoku:init/1
(2)spawn与spawn_link的主要区别
这是并发编程的基本问题。Erlang把创建进程,叫做“spawn”的用意,是为了突出进程的主从关系。“spawn”是孵化的意思,产生的是子进程。
创建进程的函数有2个,spawn与spawn_link。二者的主要区别在于,spawn出的进程意外崩溃后,不影响父进程的运行;spawn_link出的进程意外崩溃后,则连带父进程一并停运。
(3)GUI子进程的初始化与停运
本模块中有一函数init/1,它掌控着GUI子进程的初始化,和程序进程的停运。这个函数做5件事:
①调用模块sudoku_gui的函数new/1
②监测函数sudoku_gui:new/1的运行用时
③接收来自sudoku_gui的消息{gfx,GFX}
④调用sudoku_game:init(GFX)
⑤根据sudoku_game:init(GFX)返回的值,确定进程的终止或异常退出
第①和第②这两件事情,是用“宏” ?TC(sudoku_gui:new(self()))完成的,它是个函数调用的嵌套语句,很有Erlang的特色。
宏TC定义在sudoku.hrl
-define(TC(Cmd), tc(fun() -> Cmd end, ?MODULE, ?LINE)).
把它展开,就是 tc(fun() -> sudoku_gui:new(self()) end, ?MODULE, ?LINE)
函数tc/3是在本模块中定义的,第一参数是个函数,第二参数?MODULE是本模块名字(sudoku),第三参数是该语句所在的行。
可以在函数tc/3中显示一下,会看清楚:
tc(Fun,Mod,Line) ->
    io:fwrite("~ntc(~p,~p,~p)~n",[Fun,Mod,Line]),
    ……
第③件事情,语句 receive {gfx, GFX} -> ok end,接收来自sudoku_gui的消息。
为什么说是来自sudoku_gui的消息呢?函数调用sudoku_gui:new(self())中的参数self,本进程的Pid,告知对方是为了通信联络。
可用语句io:fwrite("~nself() = ~p~n",[self()])显示看看Pid的尊容,如:self() = <0.41.0>
第④和第⑤两件事情,是由一个case结构完成的:
case sudoku_game:init(GFX) of
    Halt -> erlang:halt();
    Stop -> exit(Stop)
end.
涉及sudoku_gui和sudoku_game两个模块的事情,以后细聊,这里只说本模块内的事儿。
这个case主管主进程的终止退出。Halt的值是原子halt或keep,来自本模块的函数start()或go()。
erlang:halt()进程正常终止,exit(Stop)进程异常退出,Stop是异常的信息提示。
(4)有意思的函数tc/3
这个函数名字的意思是time counter,“计时器”。
这个函数也很能体现Erlang函数编程特色。
timer:tc(erlang, apply, [Fun,[]]),是个高阶函数,即把函数当成参数值使用的函数。
apply和Fun都是函数。apply是erlang内建函数,Fun是sudoku_gui:new(self())。
我们做个实验,看看apply()和timer:tc()的工作情况。
    49> W= fun(X)-> io:fwrite("~n~s~n",[X])end.
        #Fun<erl_eval.6.13229925>
    50> apply(W,["i am a worker"]).            
        i am a worker
        ok
    51> 
    52> timer:tc(erlang, apply, [W,["kjkjkjkjk"]]). 
         kjkjkjkjk
         {100000,ok}
    53> 
case timer:tc(erlang, apply, [Fun,[]]) of
    {_,{'EXIT',Reason}} -> exit(Reason);
    {T,R} ->
        io:format("~p:~p: Time: ~p/n", [Mod, Line, T]),
        R
end.
case中函数Fun执行的两个结果,{_,{'EXIT',Reason}}是发生异常,将导致退出exit(Reason);
另一结果{T,R},即{Time,Result},正常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值