本系列文章是《Programming Erlang》的学习笔记
Erlang是什么
Erlang是由Ericsson开发的一种面向并发环境的函数式编程语言,
Erlang的并发性是语言虚拟机支持的,不依赖于OS。
Getting Stared
Erlang是面向并发环境的编程语言,一个Erlang程序是由多个并行的进程构成的。进程执行模块中定义的函数。一个erl文件包含一个模块,编译后的erl文件就可以在shell中直接执行了。
-module(hello).
-export([start/0]).
% Hello erlang
start() ->
io:format("Hello Erlang!~n").
进入Erlang Shell(erl),就可以运行这个模块了
使用c(hello).编译hello模块使用hello:start().调用start函数,即可看见运行结果
$ erl
1> c(hello).
{ok, hello}
2> hello:start().
Hello Erlang~
ok
3> halt().
$
这是一个最简单的Erlang模块,我们可以通过这个模块学习一下Erlang的基本语法。
- 不难发现,所以Erlang语句都以.结尾,一个Erlang语句以.+空白结束,空白可以是换行符,空格,tab,等等
- -module(hello).声明了一个名为hello的模块
- -export([start/0]).是这个模块的导出函数列表,声明了可以在模块外部调用的函数列表,start/0表示start函数有0个参数
- start() -> io:format("Hello Erlang!~n").是start函数的定义,io是erlang系统提供的模块,format是io模块导出的一个函数。因此,Erlang中调用函数的基本模式即为<module>:<function>(params).
Getting little sophisticated
Hello Erlang模块阐述了一个最基本的Erlang模块,和所有Hello World!程序一样,这并没有什么卵用。我们需要一个更复杂的程序来展示Erlang的基本编程模式。
%file_server.erl
-module(file_server).
-export([start/1, loop/1]).
start(Dir) ->
spawn(file_server, loop, [Dir]).
loop(Dir) ->
receive
{Client, list_dir} ->
Client ! {self(), file:list_dir(Dir)};
{Client, {get_file, File}} ->
Full = filename:join(Dir, File),
Client ! {self(), file:read_file(Full)};
{Client, {put_file, File, Content}} ->
Full = filename:join(Dir, File),
Client ! {self(), file:write_file(Full, Content)}
end,
loop(Dir).
原子和变量,Erlang中变量以大写字母开头,小写字母开头的名称被称为原子,原子不是变量,而是符号常量,file_server程序中,list_dir,get_file,put_file都是原子而不是变量,模块名也是原子
spawn函数用于启动一个进程,并返回一个进程标识符。基本模式为spawn(<Module>, <Function>, [params])
self()获取当前进程的pid,erlang进程间通信依赖于pid
!用于向另一个进程发送消息,Erlang对现实的建模是通过这种轻量的进程间通信实现的,进程间交互使用消息。语法为<pid> ! <Message>
-
接收消息,仅仅有发送消息是不够的,还需要接收消息的语法来完成基于消息的进程间通信,接受消息的语法如下。
receive {From ,Message} -> Handle the message
From记录了消息的来源,Message是消息本身,后续是消息的处理逻辑
-
多种消息的模式匹配,file_server的代码中可以看见,receive部分有多种模式,不同消息会进入到不同的处理代码中,于是这儿的模式是这样的。
receive Parttern1 -> Actions1; Parttern2 -> Actions2 %模式之间以;分隔,最后一种模式后面不能写;
循环,Erlang是一种函数是编程语言,这意味着其逻辑结构是由一系列的函数演算构成的,和其他函数式语言类似,Erlang没有专门的循环语法,因此循环是通过递归实现的。file_server的代码中可以看见loop函数最后递归调用了自己。递归代码总会有递归栈占用过大的问题,编写成尾递归可以借由编译器的尾递归优化解决这一问题。
上述内容已经涵盖了erlang的基本知识,可以继续写出下面的file_client模块
%file_client.erl
-module(file_client).
-export([ls/1, get_file/2, put_file/3]).
ls(Server) ->
Server ! {self(), list_dir},
receive
{Server, FileList} ->
FileList
end.
get_file(Server, File) ->
Server ! {self(), {get_file, File}},
receive
{Server, Content} ->
Content
end.
put_file(Server, File, Content) ->
Server ! {self(), put_file, File, Content}},
receive
{Server, Result} ->
Result
end.
file_client.el中没有引入新的内容,在shell中运行即可看见运行结果
$ erl
1> c(file_server).
{ok, file_server}
2> c(file_client).
{ok, file_client}
3> FS = file_server:start(".").
<0.45.0> 这是FS这个进程的pid
4> file_client:ls(FS).
{ok,["file_client.beam","file_client.erl",
"file_server.beam","file_server.erl","hello.erl"]}
5> file_client:get_file(FS, "hello.erl").
{ok,<<"-module(hello).\n-export([start/0]).\n\nstart() ->\n io:format(\"Hello World!~n\").\n">>}
6> file_client:put_file(FS, "haha", "hehe").
ok
7> file_client:ls(FS).
{ok,["afile_client.beam","afile_client.erl",
"afile_server.beam","afile_server.erl","haha","hello.erl"]}
8> file_client:get_file(FS, "haha").
{ok,<<"hehe">>}
上述的file_server,file_client,涵盖了Erlang的基本语法,模式。下一篇文章会讲述Erlang语言在编写传统顺序程序时的用法。