erlang_base01_语法

##erlang学习总结

###1、退出,输入 haut().

###2、-module

###3、module_name:function_name( arguments )

例如调用tut:double(10),说明调用tut模块的double函数。

###4、模块名为tut

-module(tut).

-export( [double/1, fact/1] ).

double(X)->

2 * X.

fact(1) ->

1;

fact(N) ->

N*fact(N-1).

分号表示函数还未结束。

点号表示函数已经结束。

变量必须以大写字母开头,小写开头的为字符串。

###5、元组

可以返回多个值

-module(tut).

-export( [double/1] ).

double(X) ->

{X*2, X*3, X*4}.

###6、列表赋值

10> [One,Two|Rest]=[1,2,3,4,5,6].

[1,2,3,4,5,6]

11> One.

1

12> Two.

2

13> Rest.

[3,4,5,6]

14>

###获取列表的长度

-module(tut).

-export( [get_length/1] ).

get_length([] ) ->

0;

get_length([First|Rest]) ->

1+get_length(Rest).

###字符串输出采用ASCII表示

>[97,98,99].

"abc"

###7、百分号表示注释

###8、格式化输出

标准函数io:format

io:format("my name is ~w, my age is ~w~n", [biao, 20]).

###9、/返回浮点数

div 整数除  rem 求余

###10、‘a‘ 等同于 a

###11、元组嵌套赋值

1>F = {first, guo}.

{first,guo}

2> L = {last, biao}.

{last,biao}

3> Name = {person, F, L }.

{person,{first,guo},{last,biao}}

###12、元组元素提取,采用占位符(下划线)

6> Name.

{person,{first,guo},{last,biao}}

7> {_,{_,N1},{_,N2}} = Name.

{person,{first,guo},{last,biao}}

8> N1.

guo

9> N2.

biao

10>

###13、获取列表的元素值 [H|T]

10> ThingstoBuy=[{apple, 5}, {pear,10}, {orange, 20} ].

[{apple,5},{pear,10},{orange,20}]

11> [Buy1|Others] = ThingstoBuy.

[{apple,5},{pear,10},{orange,20}]

12> Buy1.

{apple,5}

13> Others.

[{pear,10},{orange,20}]

###14、字符串=整数列表

Hello = "hello"

整数列表中,所有的整数都是可打印字符时,才转为字符串。

15> [1,2,3].

[1,2,3]

17> [97,98,99].

"abc"

使用$来表示一个字符的ASCII值(Latin-1)

18> [$;s-32, $amp;i, $;r].

"Sir"

又例如

23> [H|T]="cat".

"cat"

24> H.

99

25> T.

"at"

###18、释放所有的变量绑定。f().

###19、Erlang提供的命令。

pwd()打印当前路径,

###切换当前目录到C盘根目录

1>  cd("e:/erlang_src").

<input type="button" e:/erlang_src

ok

###20、fun定义匿名函数,并可以赋给一个变量。类似函数指针。

4> Double = fun(X)->2*X end.

AT#Fun<erl_eval.6.80247286>

15> Double(2).

4>

###21、把fun作为参数传入

例如标准库中的lists:map(F,L).

把F应用到列表的每个元素,并返回新列表。

17> lists:map(Double, [1,2,3,4]).

[2,4,6,8]

###标准库lists:filter(P, L ).

把列表的每个元素作为P的参数,返回为TRUE的,才被放到结果列表中。

小例子:查找奇数列表中的奇数。

是否等于的测试符号:" =:= "

25> Even = fun(X) -> X rem 2 /= 0 end.

#Fun<erl_eval.6.80247286>

26> Even(2).

false

27> lists:filter(Even,[1,2,3,4,5,6,7,8,9]).

[1,3,5,7,9]

###22、比较操作符

全等于 =:=   例如 1=:=1.0 false

等于 ==  例如 1==1.0 true

不等于 /=

###23、返回fun的函数。

28> Fruits=[apple,orange,pear].

[apple,orange,pear]

29> Test=fun(L)->( fun(X) -> lists:member(X,L) end) end.

#Fun<erl_eval.6.80247286>

31> Isfruits = Test( Fruits ).

#Fun<erl_eval.6.80247286>

32> Isfruits( apple ).

true

###23、for循环,Erlang中无循环控制,需要自定义的控制结构

-module(for).

-export([for/3]).

for(MAX,MAX,F) -> [F(MAX)];

for(I,MAX,F) -> [F(I) | for(I+1,MAX,F)].

39> c(for).

{ok,for}

40> for:for(1,10,fun(I)->I*2 end).

[2,4,6,8,10,12,14,16,18,20]

在模块中,

如果使用-import(for, [for/3] ) 在使用for函数时,就不需要指定模块名。

###24、列表解析

L=[1,2,3,4,5,6].

[1,2,3,4,5,6]

42> [2*X || X <- L].

[2,4,6,8,10,12]

43>

例如

[2*X || X <- L]

由F(X)组成的列表,其中X是L的每个元素。

又例如

49>  L=[{orange, 8}, {apple, 5}, {pear, 10} ].

[{orange,8},{apple,5},{pear,10}]

50>  [{Name, Number*2} || {Name,Number} <- L].

[{orange,16},{apple,10},{pear,20}]

注意{Name,Number}用于和L中的元素进行匹配。

###25、过滤器

51> [X || {a, X} <- [{a,1}, {b,2}, {a,3}] ].

[1,3]

过滤出为a的值。

过滤出奇数的另外一种实现。

53> L = [1,2,3,4,5,6,7,8,9].

[1,2,3,4,5,6,7,8,9]

54> [X || X <- L, X rem 2 /= 0].

[1,3,5,7,9]

55>

[X || QUALIFIER1,QUARLIFIER2...].

QUALIFIER1...如果是  X <- L形式则为生成器。

如果是布尔表达式则为过滤器。

###26、快速排序算法

-module(qsort).

-export([qsort/1]).

qsort([]) -> [];

qsort([Head|L] ) ->

qsort([X || X <- L, X < Head])

++ [Head] ++

qsort([X || X <- L, X >= Head] ).

###27、lists:seq(1,N) 返回1-N的整数列表。毕达哥拉斯三元组。

###28、断言。可以用于简单的变量测试和比较。断言语句使用when关键字开头。

-module(maths).

-export([max/2]).

max(X, Y) when X > Y -> X;

max(X, Y) -> Y.

###29、断言谓词。

is_atom(X)

is_integer(X)

is_list(X)

is_function(X,N)

is_tuple(X) 是否元组

is_record(X,Tag,N)

###30、断言内建函数(BIF)

abs(X)

element(N,X) 元组X的第N个元素

hd(X) 列表X的头部

tl(X) 列表X的尾部,除第一个元素以外的剩余元素。

length(X) 列表的长度

node() 当前节点  node(X) 创建节点

round(X) 四舍五入为整数

self() 当前进程的进程标示符

size(X) 元组的大小

例如:

67> L=[1,2,3,4,5,6,7].

[1,2,3,4,5,6,7]

68> hd(L).

1

69> tl(L).

[2,3,4,5,6,7]

###31、用分号分隔的断言表示 or

用逗号分隔的断言表示 and

assert(X,Y) when is_integer(X), X>5, X>Y -> [X,Y];  % and 操作

assert(X,Y) -> [Y,X].

###32、andalso,orelse用于构建稍微复杂一些的断言。

###33、记录的定义保存在.hrl文件中,这样可以被多个module共享,以保证是同一份定义。

在命令行中使用记录之前,先要读取记录hrl文件,命令为 rr("xxx.hrl").

###34、record定义,表名字为todo,字段有status,who,memo

-record( todo, {status=ok, who = biao, memo=test}).  -->保存在record.hrl中.

插入三条记录

76> X1=#todo{status=bad,who=zb,memo=test2}.

\#todo{status = bad,who = zb,memo = test2}

77> X2=#todo{}.

#todo{status = ok,who = biao,memo = test}

78> X3=X2#todo{who=biaobiao}.       %复制一条记录,然后修改who的值

#todo{status = ok,who = biaobiao,memo = test}

rf(todo).

去掉记录的定义,然后打印X2,会发现其实是元组。

###35、提出RECORD记录值。

第一种方法。#todo{who=W, memo=M, status=S} = X3.

第二种方法。X3#todo.W    X3#toto.M

###36、case..of..end分支

例子:自定义filter

filter(P, [H|T]) ->

case P(H) of

true -> [H|filter(P, T)];

false->filter(P,T)

end;

filter(P,[]) -> [].

13> IsBig = fun(X) ->  X > 10 end.

#Fun<erl_eval.6.80247286>

14> maths:filter(IsBig, [1,3,4,11,20]).

[11,20]

###37、条件分支if..end

iftest(X) ->

if

X == 1 ->

"return 1";

X == 2 ->

"return 2";

true ->

"return other"

end.

###38、try .of. catch.. after..end  为 case的增强版,带有异常捕捉功能。

三种类型的异常exit/throw/error

异常捕捉例子:

-module(try_test).

-export([demo1/0,generate_exception/1,catcher/1]).

generate_exception(1) -> a;

generate_exception(2) -> throw(a);

generate_exception(3) -> exit(a);

generate_exception(4) -> {‘EXIT‘, a};

generate_exception(5) -> erlang:error(a).

demo1() ->

[catcher(I) || I <- [1,2,3,4,5]].

catcher(N) ->

try generate_exception(N) of

Val -> { N, normal, Val}     %%如果没有异常,这个generate_exception(N)的值就和VAL进行匹配。

catch

throw:X -> {N, caught, throw, X };

exit:X -> {N, caught, exit, X };

error:X -> { N, caught, error, X }

after

io:format("test").

end.

运行结果:

6> try_test:demo1().

[{1,normal,a},

{2,caught,throw,a},

{3,caught,exit,a},

{4,normal,{‘EXIT‘,a}},

{5,caught,error,a}]

###39、erlang:error提高错误信息的质量。

sqrt(X) when X < 0 ->

erlang:error( { sqrtNagativeArgumentError, X } );

sqrt(X) ->

math:sqrt(X).

运行后,有如下提示:

7> maths:sqrt(-1).

** exception error: {sqrtNagativeArgumentError,-1}

in function  maths:sqrt/1

###40、函数返回值,

{ok, Val} 或者  {error, Why}  调用者模式匹配后进行处理。

例如:

###41、erlang中的小于等于:" =< ", 大于等于: ">="

例如:

%%输出不同的结果

getValue(X) when X >= 1, X =< 100

-> {ok, ‘less than 100‘};

getValue(X)

-> {error, ‘larger than 100‘ }.

%%调用getValue对结果输出

deal(X) ->

case getValue(X) of

{ok, Info } ->

io:format("ok print info: ~w~n", [Info]);

{error, Info} ->

io:format("error print info: ~w~n", [Info])

end.

###42、捕获任何异常的写法 ( _:_ )。

try expr of

模式匹配 -> ...

catch

_:- ->  异常处理

end.

###43、使用 erlang:get_stacktrace() 打印函数调用的栈信息。

demo2() ->

try generate_exception(2)

catch

throw:X ->

{X, erlang:get_stacktrace()}

end.

###44、BIF并非erlang编写,而是虚拟机上的基本操作,包含在erlang模块中,

并自动导入,所以使用时不需要用前缀,例如 erlang:tuple_to_list/1

cat 和 ‘cat‘ 是一样的,为一个常量。

元组转化为列表

3> tuple_to_list({1, "cat", "hello"}).

[1,"cat","hello"]

获取当前时间

4> time().

{11,11,2}

###45、二进制数据中的整数,必须是0-255.

超出255,会从0开始,例如256输出会变成0, 257变成1.

erlang的函数注释:@spec func( arg1, arg2 ...) -> val

###46、列表转成二进制数据。

@spec list_to_binary( L ) -> binary().

11> list_to_binary([1,2,3]).

<<1,2,3>>

12> list_to_binary([<<1,2,3>>, 1,2,3,<<4,5,6>>]).

<<1,2,3,1,2,3,4,5,6>>

13>

###47、列表分割

@spec split_binary(BIN, POS) -> {BIN1,BIN2}

13> split_binary(<<1,2,3,4,5,6>>, 3).

{<<1,2,3>>,<<4,5,6>>}

###48、@spec term_to_binary(BIN) -> Term

把任何erlang值转成二进制,把格式也存入二进制中,用于文件传输和网络传输,

可以还原的。

17> term_to_binary({"cat", "abc"}).

<<131,104,2,107,0,3,99,97,116,107,0,3,97,98,99>>

18> binary_to_term(<<131,104,2,107,0,3,99,97,116,107,0,3,97,98,99>>).

{"cat","abc"}

###49、二进制的字节长度

@spec size(BIN) -> Int

19> size(<<1,2,3,4,5,6>>).

6

###50、比特语法

例子,用16位存储RGB

用16位存储,Red和Blue为5为,Green 6位

20> Red = 10.

10

21> Green = 20.

20

22> Blue = 30.

30

23> Color = <<Red:5, Green:6, Blue:5>>.

<<82,158>>

采用模式匹配提出值

24> <<R:5, G:6, B:5>> = Color.

<<82,158>>

25> R.

10

26> G.

20

27> B.

30

28>

###51、比特语法表达式中的元素,有一项指明计算机系统的字节序。

big/little/native.  默认为big,运行时根据CPU来确定字节序则选native

在不同机器之间进行整数、二进制之间的解包和封包,需要使用正确的字节序。

例如<<123456:32/big, 45678:16/little>>

###52、全局宏定义,使用问号获取。

-define(BUFFER, 2048).

getDefine() ->

?BUFFER * 2.

测试结果:

38> maths:getDefine().

4096

###53、apply可以动态调用BIF,例如apply(erlang, atom_to_list, [hello]).

模块属性

-import( lists, [map/2] ).

引入lists:map/2, 在模块中调用就不需要指明前缀lists

-export([getDefine/2]).

只有导出函数,在模块外部才能被访问到。

-compile(export_all).

如果要把模块中的函数全部导出,可以使用-compile来代替-export.

-vsn(1.0).

表示模块的版本。

###54、自定义的模块属性。

例如-author( biao ).

使用maths:module_info().可以输出所有这些信息项。

50> maths:module_info(attributes).

[{vsn,[1.0]}]

51> beam_lib:chunks("maths", [attributes]). %使用系统模块来获取。

{ok,{maths,[{attributes,[{vsn,[1.0]}]}]}}

从以上结果抽取对应的值。

beam_lib:chunks用来提取模块的属性,然后用以下函数可以提出属性值。

-module(extract).

-export([extract/2]).

extract(File, Key) ->

case beam_lib:chunks(File, [attributes]) of

{ok, {File,[{attributes,L}]}}  ->

case lookup( Key, L ) of

{ok, Val} -> Val;

error -> exit(notFound)

end;

_ ->

exit( badFile )

end.

lookup(Key, [{Key,Val}|_]) -> {ok,Val};

lookup(Key, [_|T]) -> lookup(Key, T);

lookup(Key, [] ) -> error.

输出:

53> extract:extract(maths, vsn).

[1.0]

###55、块表达式。

begin

expr1,

expr2

end.

返回的是最后一条expr的值。

###56、布尔表达式

63> not true.

false

64> true and false.

false

65> true or false.

true

66> (2>1) or (3>4).

true

erlang的预处理器是epp

###57、转义符

\b 退格

\d 删除

\s 空格 \t tab  \n 换行 \r回车

\^X 代表 CTRL+X ,X为A-Z或a-z

\‘单引号  \" 双引号 \\反斜杠 \C字母的ascii值。

\NNN \NN \N 表示八进制数。

###58、函数引用,使用fun funcname/argnum

-module(funRef).

-export([double/1,double2/1]).

square(X) -> X*X.

double(L)->lists:map(fun square/1, L).  %引用本地

double2(L) -> lists:map( fun maths:sqrt/1, L ). %引用其他模块

###59、包含文件,把文件引入当前模块,例如hrl文件

-include( File ).

-include_lib("kernel/include/file.hrl"). 包含lib下最新kernel下的file.hrl.

###60、中缀操作符++ --

72> [1,2,3]++[4,5,6].

[1,2,3,4,5,6]

73> [1,2,3,3,3,4,5,3]--[3,3].

[1,2,3,4,5,3]

74> [1,2,3,4,3,3]--[3,3,3,3].

[1,2,4]

###61、宏定义

-define( BUFFER, 2048).

-define( Test(A, B), {A,B,A,B}).

预定义宏。

print()->

io:format("~p,~p,~p~n", [?FILE,?MODULE,?LINE]).

87> funRef:print().

"./funRef.erl",funRef,7

宏的流程控制,编译开关。

-module(macro).

-export([start/0,loop/1]).

-ifdef(debug).

-define(TRACE(X), io:format("Trace ~p~p:~p~n", [?MODULE,?LINE,X])).

-else.

-define(TRACE(X), void).

-endif.

start() -> loop(5).

loop(0) -> void;

loop(N) ->

?TRACE(N),

loop(N-1).

编译运行:

107> c(macro,{d, debug}).  %%引入debug定义

{ok,macro}

108> macro:start().

Trace macro14:5

Trace macro14:4

Trace macro14:3

Trace macro14:2

Trace macro14:1

void

###62、在模式中使用匹配操作符。

test({name,Name}=Z|T) ->

f(Z)...

###63、K进制整数的表示方法。

15#11.

15进制,其值为16.

$a,$\^c,表示ascii值。

###64、进程字典,由一系列的键值对组成。

3> get().

[]

4> put(x,20).  %把x的值设置为20,并把原来的值返回。

undefined

5> put(x,30).

20

6> get(x).

30

7> put(y,40).

undefined

8> get().  %返回所有的进程字典

[{y,40},{x,30}]

9> erase(x).  %删除字典x

30

10> get().

[{y,40}]

11> erase().

[{y,40}]

尽量少用进程字典,一般用于只读的一些参数设置。一次性写入的变量。

###65、erlang:make_ref().创建一个唯一标签。

全局唯一。匹配时使用的。

13> Data = make_ref().

#Ref<0.0.0.64>

14> Data.

#Ref<0.0.0.64>

###66、短路布尔表达式。andalso  orelse  而 (and,or两个都要求值)。

比较表达式:

X =< Y  小于等于

X /= Y 不等于    仅适用于整数和浮点数的比较。

X =:= Y 全等于   适用于所有的比较。和C++中的==一样。

X =/= Y 不全等于  适用于所有的比较。和C++中的==一样。

X == Y  等于   仅适用于整数和浮点数的比较。

###67、下划线变量。

只使用一次的变量,例如open(File,_Mode) 等价于 open(File, _)

退出shell 输入q().等同于init:stop().

###68、加载路径:

code:add_patha(Dir).加到开头。

code:add_pathz(Dir).加到结尾。

code:all_loaded(). 已加载的路径。

code:get_path().查找路径设定值。

code:clash().检查加载错误。

命令行增加加载路径:erl -pa Dir1 -pa Dir2 ....

获取erlang所需的home目录 init:get_argument(home).

###69、命令行脚本。

erl -noshell -s hello start -s init stop.

用非交互式方式,调用hello:start(),然后调用init:stop().

命令行中执行任意一个函数

erl -eval ‘io:format("test").‘ -noshell -s init stop.

###70、一个相关的makefile

.SUFFIXES: .erl .beam

.erl .beam:

erlc -W $<

ERL= erl -boot start_clean

MODS = module1 module2 module3

all: compile

${ERL} -pa ‘./dir‘ -s module1 start

compile:${MODS:%=%.beam}

clean:

rm -rf *.beam erl_crash.dump

erlang虚拟机的错误信息。

webtool:start().可以看到地址。

1> webtool:start().

WebTool is available at http://localhost:8888/

Or  http://127.0.0.1:8888/

{ok,<0.34.0>}

###71、ERLANG中进程和操作系统是不同的,ERLANG中的进程是程序语言,并不属于操作系统。

每个进程都是独立运行的ERLANG虚拟机。

Pid = spawn(Fun).产生一个新进程对Fun求值。

Pid ! M  把消息M发送给Pid进程,返回M.

Pid1 ! Pid2 ! Pid3 ! M..群发消息。

receive ... Other .. end. 接受消息。

###72、创建一个求面积的服务进程。

-module(area).

-export([loop/0,rpc/2]).

%%客户端

rpc(Pid, Request) ->

Pid ! {self(), Request },

receive

{Pid, Response} ->

io:format("get response: ~p~n", [Response]);

_Other ->

_Other

end.

%%服务端

loop() ->

receive

{From, {rectangle, W, H}} ->

From ! {self(), W * H},

loop();

{From, {circle, R} } ->

From ! {self(), 3.14*R*R},

loop();

{From, _Other} ->

From ! {self(),"i don‘t know~~"},

loop()

end.

运行情况:

14> Pid = spawn(fun area:loop/0).

<0.62.0>

16> area:rpc(Pid, {rectangle, 4, 5}).

get response: 20

ok

17> area:rpc(Pid, {circle, 3}).

get response: 28.259999999999998

ok

注意:Pid ! M 本身是会返回消息M,所以在输出中也会体现。

self()表示当前进程的进程号

###73、erlang允许的最大进程数。

18> erlang:system_info(process_limit).

32768

要增加这个上限,再启动时使用+P参数

C:\Documents and Settings\Administrator>erl +P 50000

2> erlang:system_info(process_limit).

50000

###74、计算创建进程消耗的CPU时间和实际时间。

-module(elapse).

-compile(export_all).

compute(N) ->

Max = erlang:system_info( process_limit ),

io:format("max process limit is: ~p~n", [Max] ),

statistics(runtime),  %%开始统计CPU消耗时间

statistics(wall_clock),  %%开始统计实际消耗时间

L = for( 1, N, fun() -> spawn( fun() -> wait() end ) end  ), %%启动N个进程。

io:format("in"),

{_,Time1} = statistics( runtime ),       %%统计时间

{_,Time2} = statistics( wall_clock ),    %%统计时间

lists:foreach( fun( Pid ) -> Pid ! die end, L ),

U1 = Time1 * 1000/N,

U2 = Time2 * 1000/N,

(pse ~p(um) CPU Time and ~p(um) Wall Time.", [U1, U2] ).

wait() ->

receive

die -> void

end.

for( N, N, F ) -> [F()];

for( I, N, F ) -> [F() | for(I+1,N,F)].  %%注意这里的for,不要写成F

###75、receive 进程等待的超时时间设置。

-module(wait).

-compile( export_all ).

waiting( Time ) ->

receive

Time when Time > 100 ->

‘time is larger than 100‘;

_ ->

‘other time‘

after Time ->

‘no request...‘

end.

等待Time毫秒后,如果没有接收到合适的请求,则执行after后面的语句。

after后面的箭头,极易遗漏!

###76、只有超时的receive语句

例如让当前的进程停止T ms

-module(sleep).

-export([sleep/1]).

sleep(T) ->

receive

after

T -> void

end.

###77、永远等待 infinity

sleep()->

receive

after

infinity ->

void

end.

###78、注册进程:发布一个进程标示符,以便其他进程与之通信。

register(AnAtom, Pid ).

unregister(AnAtom).

whereis(AnAtom) -> Pid | undefined 判断原子AnAtom是否已被注册。

registered() -> [] 返回系统中所有已注册的名称列表。

例如:

6>  Pid = spawn(fun area:loop/0).

<0.42.0>

7> register( area, Pid).

可以看做是别名,进程退出时,自动取消注册。

###79、spawn( Fun) 创建一个进程、执行Fun对应的函数。

-module(clock).

-compile(export_all).

start(Time, Fun) ->

register( clock, spawn( fun() -> tick( Time, Fun) end ) ).   %%创建一个进程,并给出别名,然后进程执行tick函数。

stop() ->

clock ! stop.

tick( Time, Fun ) ->

receive

stop -> void

after

Time ->                     %% 不断地等待,超时后打印一条记录,又接着回调tick

Fun(),

tick(Time, Fun)

end.

执行结果如下:

27>

clock:start(1000, fun()->io:format("test") end ).

true

28> test28> test28> test28> test28> test28> test28> test28

28> test28> test28> test28>

clock:stop().

teststop

###80、使用MFA创建进程。

spawn( Mod, Function, Args ).

link(Pid) 把当前进程和Pid进程链接。

unlink(Pid) 取消链接

exit(Why)  退出,并广播这个Why原因内容。

exit(Pid, Why) 向Pid发送退出信号。

erlang:monitor(process, Pid) 建立一个监视器。监视器是单向的,而链接是双向。

BIF函数:process_flag( trap_exit, true).把当前进程变成系统进程。

系统进程可以捕获别的进程的退出状态。

###81、

进程正常结束,发出normal信号,他的进程集合屏蔽这种信号。

进程非正常结束,他的进程集中的非系统进程都会退出,系统进程才能处理这种信号。

进程收到kill信号,不管是否是系统进程,全部退出,并且广播。

###82、三种进程模式

Pid = spawn( fun() -> ... end ). 被创建的进程消亡,当前进程毫无察觉。

Pid = spawn_link( fun() -> ... end ).被创建的进程非正常消亡,当前进程也会退出。

被创建的进程退出,当前进程做错误处理。

process_flag(trap_exit, true),  %先变成系统进程。

PID = spawn_link( fun() -> .. end ),

loop().

loop( State) ->

receive

{‘EXIT‘, Pid, Reason } ->

...

loop( State1 );

...

end.

###83、存活进程例子

-module(onexit).

-compile( export_all).

keep_alive( Name, Fun )->   %创建一个进程注册为Name,然后执行Fun函数。

register( Name, Pid = spawn( Fun) ),

on_exit( Pid, fun(_Why) -> keep_alive(Name, Fun) end ).

% 把当前进程和Pid链接,然后进程异常退出时,捕获并调用Fun函数处理。

on_exit(Pid, Fun) ->

process_flag( trap_exit, true),

link( Pid ),

receive

{ ‘EXIT‘, Pid, Why } -> Fun(Why)

end.

###84、创建一个名字服务。

-module(kvs).

-compile(export_all).

%启动:创建一个服务进程,循环等待处理,并给予别名kvs

start() ->

register(kvs, spawn( fun() -> loop() end )).

%向服务端发起变更请求,并接收结果

store(Key, Value) ->

kvs ! {self(), {store, Key, Value}},

receive

{kvs, Reply} -> Reply

end.

%向服务器发起查询请求,并接收结果

lookup(Key) ->

kvs ! {self(), {lookup, Key}},

receive

{kvs, Reply} -> Reply

end.

%接收客户端请求,并把结果发给客户端

loop()->

receive

{From, {store, Key,Value} } ->

put( Key, Value ),

From ! {kvs, true},

loop();

{From, {lookup, Key} } ->

From ! {kvs, get(Key) },

loop()

end.

运行结果:

18> kvs:start().

true

19> kvs:store(money, 100).

true

20> kvs:store(name, biao).

true

21> kvs:lookup(name).

biao

22> kvs:lookup(money).

100

###85、启动节点时,给予名字。同一台机器。

erl -sname Name

C:\Documents and Settings\Administrator>erl -sname biao1

Eshell V5.8.4  (abort with ^G)

([email protected])1> cd ("e:/erlang_src").

e:/erlang_src

ok

在第一个节点上运行服务

([email protected])2> kvs:start().

true

在第二个节点上,远程发送消息来调用

([email protected])3> rpc:call([email protected], kvs, store, [sex, male]).

true

([email protected])4> rpc:call([email protected], kvs, lookup, [name]).

biao

###86、在局域网内的不同机器之间。

erl -name biao1 -setcookie abc

运行在两个不同的网络上,需要用全名。需要DNS服务。

两个节点之间需要使用相同的cookie。

在/etc/hosts可以添加域名入口,内容格式为 网络IP地址 主机名或者域名  [主机名别名]。

erl -sname biao1

表示短名,在同一台机器或者在局域网内,直接用短名即可。

###87、判断节点(NODE)之间是否连通。

net_adm:ping(Node).

设置erlang节点的cookie

erlang:set_cookie(node(), abc).

spawn( Node, Fun ) -> Pid

spawn( Node, Mod, Func, Arg ) -> Pid

spawn_link( Node, Fun ) -> Pid

spawn_link( Node, Mod, Func, Arg) -> Pid.

node()->Node. 返回本地节点的名字。

nodes() ->Node[] 返回与当前节点连接的所有节点。

monitor_node(Node, Flag ) 当前进程监视Node,如果Flag为true则打开,Flag为false则关闭。

当前进程会收到信号{nodeup, Node}, {nodedown, Node }

is_alive() -> bool() 本地节点状态是否正常。

{RegName,Node} ! Msg 向节点Node的RegName进程发消息。

###88、在因特网上两个节点通信

A、确保4396端口是通的,Erlang的epmd会使用这个端口。

B、ERLANG端口使用。启动时erl -name .. -setcookie .. -kernel inet_dist_listen_min Min inet_dist_listen_max Max.

C、节点之间必须拥有相同的cookie,具有相同cookie的相连接的节点群称为ERLANG集群。

###89、apply(M,F,A),这个可以实现,模块函数的配置化。

rpc:call(Node, Mod, Function, Args) -> Result | {badrpc, Reason}

erl -setcookie ABCEDEF

###90、lib_chan模块,用来控制能够启动哪些进程。这个应该是第三方的包。

start_server()->true  启动一个服务。

start_server( Conf) -> 按照配置启动一个服务。

配置文件内容是一系列的元组,

{port, NNN }  服务器的监听端口为NNN

{service, S,password, P, mfa, SomeMod,SomeFunc,SomeArgs}

定义由密码P保护的服务S,如果服务启动,则由SomeMod:SomeFunc(MM,ArgsC,SomeArgs}创建的进程会出处理来自客户端的消息。

MM: 代理进程Pid,用于向客户端发消息

ArgC:来自客户机的调用参数。

connect(Host, port, S, P, ArgC) -> {ok, Pid} | {error, Why}

###91、Socket套接字

-module(socket_test).

-compile(export_all).

get_url() ->

get_url( "www.google.com" ).

get_url( Host ) ->

%% 调用connect连接Host:80,产生一个套接字,

%% 采用二级制传输,{packet, 0}意味着原封不动地返回TCP数据。

{ok, Socket} = gen_tcp:connect( Host, 80, [binary, {packet, 0}] ),

%% 把消息发送到套接字

gen_tcp:send( Socket, "Get/HTTP/1.0\r\n\r\n"),

%% 进程接收套接字返回的消息

receive_data( Socket, [] ).

receive_data( Socket, Result ) ->

receive

%% 每次收到的Bin消息都放到列表的头部

{tcp, Socket, Bin } ->

receive_data( Socket, [Bin|Result] );

%%接收完毕,把数据反转并转化为二级制数据

{tcp_closed, Socket } ->

list_to_binary( lists:reverse(Result) )

end.

运行情况:

8> socket_test:get_url().

<<"HTTP/1.0 405 Method Not Allowed\r\nContent-Type: text/html; charset=UTF-8\r\n

Content-Length: 11815\r\nDate: Fri, 19 Aug 20"...>>

9>

###92、创建一个监听8888端口的服务

服务端

-module(compute_server).

-compile(export_all).

start_server() ->

%% 监听8888端口,并返回一个监听套接字Listen

%% 字节流中的头部是4个字节

{ok,Listen} = gen_tcp:listen(8888, [binary, {packet, 4}, {reuseaddr, true}, {active, true}]),

%%开始监听8888端口,等待连接

%%有新连接进来,产生一个新的连接套接字Socket

{ok, Socket} = gen_tcp:accept(Listen),

%%连接成功后,关掉监听器,新建的Socket不会受到影响。

gen_tcp:close( Listen ),

loop( Socket ).

loop( Socket ) ->

receive

{tcp, Socket, Bin } ->

io:format("receive binary: ~p~n", [Bin]),

Str = binary_to_term( Bin ),

io:format("receive value: ~p~n", [Str]),

Reply = Str,

io:format("server reply: ~p~n", [Reply]),

gen_tcp:send(Socket, term_to_binary(Reply) ),

loop( Socket );

{tcp_closed, Socket } ->

io:format("server socket closed.~n")

end.

客户端:

-module(compute_client).

-compile(export_all).

send( Str ) ->

{ok, Socket} = gen_tcp:connect("localhost", 8888, [binary, {packet, 4}]),

ok = gen_tcp:send(Socket, term_to_binary( Str) ),

receive

{tcp, Socket, Bin } ->

io:format("client receive binary: ~p~n", [Bin] ),

Val = binary_to_term( Bin ),

io:format("client receive value: ~p~n", [Val] ),

gen_tcp:close( Socket )

end.

运行情况

客户端

2> compute_client:send("abc").

client receive binary: <<131,107,0,3,97,98,99>>

client receive value: "abc"

ok

服务端

7> compute_server:start_server().

receive binary: <<131,107,0,3,97,98,99>>

receive value: "abc"

server reply: "abc"

server socket closed.

ok

###93、主动套接字

创建时设置{active, true}

数据到达时,系统向控制进程发送{ tcp, Socket, Data }

控制进程无法控制流量。

用于异步服务器,客户端不会被阻塞。

###94、被动套接字

{actvie, false}

gen_tcp:recv(Socket, N) 接收来自套接字的数据。N为接收的字节数,如果N=0,则接收所有的

调用recv时,客户端会被阻塞。

只能等待一个套接字的消息。

###95、混合型Socket

{active, once }

主动接收一条消息,然后系统处于阻塞状态,

必须调用inet:setopts(Socket, [{active, once}] )

###96、inet:peername(Socket) ->{ok, {Ip, Port}} | {error, Why} 查看连接的来源。

###97、ets 表类型

set 键不能相同。

order set 排序的set

bag  可以有相同的键,但不能有两个相同的元组。

duplicate bag 可以有相同的键值,也可以有两个相同的元组。

###98、set ordered_set,bag,duplicat_bag 操作

-module(ets_test).

-export([start/0]).

start() ->

%%注意第一个参数为 fun test/1,即调用test函数

lists:foreach( fun test/1, [set, ordered_set, bag, duplicate_bag]).

test( Mode ) ->

%%创建一个tab表,返回表ID

TabID = ets:new( tab, [Mode] ),

ets:insert(TabID, {a, 1}),

ets:insert(TabID, {b, 2}),

ets:insert(TabID, {a, 1}),

ets:insert(TabID, {a, 3}),

ets:insert(TabID, {a, 2}),

%% 把表数据转成列表。

List = ets:tab2list(TabID),

%% 13位宽度显示。

io:format("~13w => ~p~n", [Mode, List]),

%%删除表,释放空间

ets:delete( TabID ).

运行:

4> ets_test:start().

set => [{b,2},{a,2}]

ordered_set => [{a,2},{b,2}]

bag => [{b,2},{a,1},{a,3},{a,2}]

duplicate_bag => [{b,2},{a,1},{a,1},{a,3},{a,2}]

ok

###99、@spec ets:new(Name, [Opt]) -> TabID

Name 一个原子

Opt取值有

1) set | ordered_set | bag | duplicate_bag

2) private 私有表,只有所有者进程才能读写。

3) public 公开表

4) protected 只有所有者进程能写,其余知道表名字的进程可以读。

5) named_table 命名表,后续可以使用Name来操作这个表

6) {keypos,1} 设置键的位置。在record中使用。

ets:new的默认设置为

[set,protected, {keypos, 1}]

###100、把服务器的公共部分、不怎么变化的部分放在一个文件中,

然后把变化的部分(业务逻辑)放在另外一个文件中,

通过回调实现解耦。

服务端例子:

-module(server1).

-export([start/2,rpc/2]).

%% 创建一个进程,并且进程注册名是Name,传入一个Mod模块名。

start(Name, Mod ) ->

register( Name, spawn( fun() -> loop(Name, Mod, Mod:init() ) end ) ).

%% 进程Name, 循环接收消息,并调用Mod:handle(..,State)

loop( Name, Mod, State ) ->

receive

{ From, Request } ->

{Response, State1} = Mod:handle(Request, State),

From ! {Name, Response},

loop( Name, Mod, State1)

end.

%% 客户端向Name进程发送Request消息

rpc( Name, Request ) ->

Name ! {self(), Request},

receive

{Name, Response} -> Response

end.

被回调的Mod模块

-module(name_server).

-compile(export_all).

-import(server1, [rpc/2]).

%%被服务器回调的程序代码,可以把服务器代码当做一个模板

init() -> dict:new().

handle( { add, Name, Place }, Dict ) ->

{ok, dict:store( Name, Place,Dict) };

handle( { whereis, Name }, Dict ) ->

{dict:find(Name, Dict), Dict}.

被回调的部分可以看做是接口。

运行情况如下:

10> server1:start(myserver, name_server).

true

11> server1:rpc(myserver, {add, biao, ‘at home‘}).

ok

12> server1:rpc(myserver, {whereis, biao}).

{ok,‘at home‘}

###101、支持事务的服务端程序

在handle处理异常时,给客户端发退出指令。

-module(server2).

-compile(export_all).

%% 创建一个进程,并且进程注册名是Name,传入一个Mod模块名。

start( Name, Mod ) ->

register( Name, spawn( fun() -> loop(Name, Mod, Mod:init())end )).

%% 进程Name, 循环接收消息,并调用Mod:handle(..,State)

loop( Name, Mod, OldState ) ->

receive

{From, Request } ->

try Mod:handle( Request, OldState ) of

{Response, NewState} ->

From ! {Name, ok, Response },

loop( Name, Mod, NewState )

catch

_:Why ->

io:format("Server ~p request ~p~n"

"caused exception ~p~n",

[Name, Request, Why] ),

From ! {Name, crash },

loop( Name, Mod, OldState )

end

end.

%% 客户端向Name进程发送Request消息

rpc( Name, Request ) ->

Name ! {self(), Request},

receive

{Name, crash} -> exit( rpc );

{Name, ok, Response} -> Response

end.

###102、一个空服务器,然后根据传入的指令进行操作。

服务端

-module(server5).

-compile(export_all).

start() ->

spawn( fun() -> wait() end ).

wait() ->

receive

{become, F} -> F()

end.

rpc( Pid, Q) ->

Pid ! { self(), Q },

receive

{Pid, Reply} -> Reply

end.

具体的服务操作,把功能部分解耦出来。

-module(fac_server5).

-compile(export_all).

loop() ->

receive

{From, {fac, N} } ->

From ! {self(), fac(N)},

loop();

{become, Something} ->

Something()

end.

fac(0) -> 1;

fac(N) -> N * fac(N-1).

运行情况:

2> Pid = server5:start().

<0.33.0>

3> Pid !  {become, fun fac_server5:loop/0 }.

{become,#Fun<fac_server5.loop.0>}

4> server5:rpc(Pid, {fac, 3}).

6

5> server5:rpc(Pid, {fac, 5}).

120

###103、服务器模板gen_server.

需要在开头使用 -behaviour( gen_server).

回调函数有:

init/1  初始化,返回一个状态,作为handle_call的输入

handle_call/3  回调主体,模式匹配。

handle_cast/2

handle_info/2

terminate/2

code_change/3

写个服务器程序,需要以下三步:

1、确定回调模块的名称。这里为my_bank.

2、接口说明

start() 打开银行

stop() 关闭银行

new_account(Who) 开户

deposit(Who, Amount) 存钱

withdraw(Who,Amount) 取钱

3、编写回调函数

init([]) -> {ok, State }  %这里的State生成后,将传给后面的函数使用,是一个全局的对象

handle_call(_Request, _From, State) -> {reply, Reply, State}

handle_cast(_Msg, State) -> {noreply, State }

handle_info(_Info, State) -> {noreply, State}

terminate(_Reason, _State) -> ok

code_change(_OldVsn, State, Extra ) -> {ok, State }

例子:

%%第一步,确定回调模块名称

-module(my_bank).

%-behaviour(gen_server).

-compile(export_all).

%%第二步,接口实现

%% start_link启动一个本地的服务器{local,Name},第二个参数为回调模块。.

%% 首先会调用MOD:init/1

start() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], [] ).

%% gen_server:call(?MODULE,Term) 发起对Name服务器的远程调用。

stop() -> gen_server:call(?MODULE, stop ).

new_account(Who) -> gen_server:call(?MODULE,{new,Who}).

deposit(Who, Amount) -> gen_server:call(?MODULE, {add, Who, Amount}).

withdraw(Who,Amount) -> gen_server:call(?MODULE,{remove, Who, Amount}).

%%第三步 回调函数实现

%%6个回调函数

init([]) ->

{ok, ets:new(?MODULE, [set]) }.

%银行开户

handle_call({new, Who}, _From, Tab) ->

Reply = case ets:lookup(Tab, Who) of

[] -> ets:insert(Tab, {Who, 0}),

{ welcome, Who };

[_] -> {Who, you_already_in_bank}

end,

{reply, Reply, Tab};

%银行存钱

handle_call({add, Who, X}, _From, Tab) ->

Reply = case ets:lookup( Tab, Who ) of

[] -> you_are_not_in_bank;

[{Who, Balance}]->

NewBalance = Balance + X,

ets:insert(Tab, {Who, NewBalance}),

{thanks, Who, your_balance_now_is, NewBalance}

end,

{reply, Reply, Tab};

%银行取钱

handle_call( {remove, Who, X}, _From, Tab ) ->

Reply = case ets:lookup( Tab, Who) of

[] -> you_are_not_in_bank;

[{Who, Balance}] when X =< Balance ->

NewBalance = Balance - X,

ets:insert(Tab, {Who, NewBalance} ),

{thanks, Who, your_balance_now_is, NewBalance};

[{Who, _Balance}] ->

{sorry, you_havnot_enough_money}

end,

{reply, Reply, Tab };

%终止服务器程序的方法 handle_call(Stop, From, State) -> {stop,Reason,Reply,Tab}

handle_call(stop, _From, Tab ) ->

{stop, normal, stopped, Tab }.

handle_cast(_Msg, State) -> {noreply, State }.

handle_info(_Info, State) -> {noreply, State }.

terminate(_Reason, _State) -> ok.

code_change(_OldVsn, State, _Extra) -> {ok, State }.

###104、gen_server:start_link(Name,Mod,InitArgs,Opts)

创建Name服务,调用Mod:init(InitArgs)启动服务。

###105、gen_server:call(Name,Request).调用服务器程序,发起Request请求,会回调handle_call

###106、gen_server:cast(Name,Name) 回调 hanle_cast(_Msg,State)

handle_cast(_Msg,State) -> {noreply, NewState}

###107、handle_info() 用来处理服务器收到的原生消息,例如收到其他进程的{‘EXIT‘,PID,WHAT}.

handle_info(_Info,State) -> {noreply,State}.

###108、在handle_XXX()函数返回{stop, Reason, State}

或者直接终结服务器返回{‘EXIT‘, reason},这些会回调terminate(Reason,NewState).

erlang_base01_语法

时间: 2024-09-30 15:48:31

erlang_base01_语法的相关文章

SqlServer给一个表增加多个字段语法

添加字段语法 alter table table_name add column_name +字段类型+ 约束条件 给一个表增加多个字段: use NatureData go alter table XunHu add MaleCount varchar(50) null, FemaleCount varchar(50) null, SubadultCount varchar(50) null, LarvaeCount varchar(50) null, TraceType varchar(50

MySQL(九)之数据表的查询详解(SELECT语法)二

上一篇讲了比较简单的单表查询以及MySQL的组函数,这一篇给大家分享一点比较难得知识了,关于多表查询,子查询,左连接,外连接等等.希望大家能都得到帮助! 在开始之前因为要多表查询,所以搭建好环境: 1)创建数据表suppliers 前面已经有一张表是book表,我们在建立一张suppliers(供应商)表和前面的book表对应. 也就是说 让book中s_id字段值指向suppliers的主键值,创建一个外键约束关系. 其实这里并没有达到真正的外键约束关系,只是模拟,让fruits中的s_id中

linux shell基础语法

1.第一个Shell脚本 打开文本编辑器,新建一个文件,扩展名为sh(sh代表shell),扩展名并不影响脚本执行,见名知意就好,如果你用php写shell 脚本,扩展名就用php好了. 输入一些代码: #!/bin/bash echo "Hello World !" "#!" 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种Shell.echo命令用于向窗口输出文本. 运行Shell脚本有两种方法. 1.1作为可执行程序 将上面的代码保存为t

Linux C中内联汇编的语法格式及使用方法(Inline Assembly in Linux C)

在阅读Linux内核源码或对代码做性能优化时,经常会有在C语言中嵌入一段汇编代码的需求,这种嵌入汇编在CS术语上叫做inline assembly.本文的笔记试图说明Inline Assembly的基本语法规则和用法(建议英文阅读能力较强的同学直接阅读本文参考资料中推荐的技术文章 ^_^). 注意:由于gcc采用AT&T风格的汇编语法(与Intel Syntax相对应,二者的区别参见这里),因此,本文涉及到的汇编代码均以AT&T Syntax为准. 1. 基本语法规则 内联汇编(或称嵌入汇

Python基本语法

Python基本语法 1.第一个Python代码 print('Hello Word!') 2.注释 1.定义:注释即解释,主要用于提示相关代码的信息. 2.注释分类:单行注释和多行注释 单行注释:# 注释内容 多行注释:''' 注释内容 ''' 或 """ 注释内容 """ 3.变量 1.定义:变量就是可以改变的量 2.命名规则: 1.汉字变量名能用,不推荐使用 2.数字不能开头 3.不可以使用特殊字符,除_ 4.变量区分大小写 5.可以使用关

Java基础语法(1)

一.Java概述 1.1Java语言平台 图1-1  java语言平台 注:开发者都是安装javaSE平台的软件. 1.2.跨平台性 平台:指的是操作系统(Windows,Linux,Mac) 跨平台:Java程序可以在任意操作系统上运行,一次编写到处运行 原理:实现跨平台需要依赖Java的虚拟机 JVM (Java Virtual Machine) 图1-2 跨平台版本 1.3      JVM   JRE   JDK说明   -A:什么是JVM JVM是java虚拟机(JVM Java Vi

eclipse使用与java语法规则

eclipse的使用 1.运行点击"三角图标"或右键Run As运行2.3. java语法规范 1.括号要成对出现2.每句代码应该有分号结束3.java语法区分大小写4.一个文件只能写一个带有public的class声明,还必须和文件名一致.一个文件中不可以有多个带有public的修饰符号5.名称写的时候不要包含关键字和非法字符(字母和下划线开头可以,也可以用数字结尾)6.java代码的语法全部都是半角符号7.学会规范的写代码. 写代码的好习惯: 1.常按保存,写完一句或几句就按一次C

js基本语法总结(一)

1.js简介 a)js是一种网页脚本语言,使得浏览器可以与网页互动. js的一种基于对象和事件驱动,具有安全性能的脚本语言,脚本语言就是在客户端的浏览器就可以互动响应处理程序的语言,而不需要服务器的处理和响应. js也可以与服务器交互响应,而服务器语言(asp.php.jsp)需要将命令上传服务器,经服务器处理后回传处理结果. js可以嵌入到html文件,不需要经过web服务器就可以对用户操作作出响应,使得网页与用户很好的交互,利用客户端电脑的资源适当减少服务器端的压力,并减少用户的等待时间.

sass语法基础知识

文件后缀名 sass有两种后缀名文件:一种后缀名为sass,不使用大括号和分号:另一种就是我们这里使用的scss文件,这种和我们平时写的css文件格式差不多,使用大括号和分号.而本教程中所说的所有sass文件都指后缀名为scss的文件.在此也建议使用后缀名为scss的文件,以避免sass后缀名的严格格式要求报错. //文件后缀名为sass的语法 body background: #eee font-size:12px p background: #0982c1 //文件后缀名为scss的语法 b