Erlang学习笔记/快速入门

Erlang学习笔记/快速入门

Erlang比较神秘,小众。函数式编程。主要适用于服务器端长连接、高并发处理,最初是爱立信针对电信系统专门设计,公认很稳定、容错好,广泛用于通信系统。

据说很多思想现在看来非常超前并且仍然超前并且不会过时。

各种原因导致不流行(函数式编程不直观,思维方式跟主流语言有差别,不善推广等)

Whatsapp用了大量erlang

国内有很多网游公司用了erlang。阿里内部有用(https://www.zhihu.com/question/46442333)。

我们常用的rabbitmq和emq都是erlang做的。

看下来至少在保持住大量长连接方面比较靠谱,还天生支持”协程“、集群、分布式。内置分布式数据库。

有必要学习一下。可能在一些基础应用上省些力气。

语法、思想确实需要适应。有的写法非常简明,有的写法就像数学公式,非常优美,比如三五行代码就可以实现一个简单的快速排序。


书籍

Programming Erlang.2Ed
erlang发明者的书

Designing for Scalability with ErlangOTP Implement Robust, Fault-Tolerant Systems
深入学习。otp等。

Erlang and OTP in Action
otp

Introducing Erlang Getting Started in Functional Programming
内容少。可快速学习或当作参考

learn_you_some_erlang_for_great_good
结构类似第一本。可作参考。


安装erlang运行环境

https://www.erlang.org/downloads
官网下载erlang环境。目前版本是OTP 23。安装后可用erlang shell直接编译和运行erlang代码。

简单的命令可用erlang shell。
复杂的可用ide。后续会介绍。

https://www.erlang.org/ 官网
https://erldocs.com/ 在线文档

windows环境变量path里添加erlang的bin目录


基本概念


模块和函数

开始用ide

ide用intellij idea社区版。
https://www.jetbrains.com/idea/download/

ide的erlang插件。本来可以在ide里装,但是被墙。只能下载后本地安装。
https://github.com/ignatov/intellij-erlang

打开ide,配置plugin,选从硬盘安装上面的插件。

新建erlang项目,src目录下新建Erlang File,选Empty module,指定模块名test。看到有几行默认代码。

改一下代码。

-module(test). 指定这个erlang模块名字是test

-export([gg/0]). 表示这个模块会暴露一个接口gg,斜杠后面是参数个数,0就是0个参数。

gg()->
io:format(“gg~n”).

定义一个函数gg,没有参数。
io是erlang自带的io库。format可打印字符。~n是换行。

ide里run。看到打印出gg。这样环境就ok了。

以后每次新学一个内容可以新建一个module,在ide中右键recompile,然后在test中调用。
也可以盯着一个module改代码。

run的配置

打开Edit Configuration,可修改运行配置。
可以看到Module and function 默认生成了test gg。
即运行test模块的gg函数。
以后要运行多个模块多个函数的话需要自行添加和修改这个config。

函数的例子1:
-module(geometry).
-export([area/1]).
area({rectangle, Width, Height}) ->
    Width * Height;
area({square, Side}) ->
    Side * Side.

同样的函数名area有两个,参数的匹配模式不一样,可以共存。注意中间是用;分隔的。

调用方式:

A = area({rectangle, 3, 4}).

会匹配第一个定义(两个rectangle匹配)

B = area({square, 12}).

会匹配第二个定义(两个square匹配)

可以进行扩展

area({circle, Radius}) -> 3.14159 * Radius * Radius.
函数的例子2:

erlang没有直接的循环。可以这样做一个循环:

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

从第二个函数开始。对I调用F,作为list的第一个元素,再把I+1,再次调for,把返回接到list后面。
如果I增加到与Max相同,则匹配到第一个for,返回F(I)。
这样实现了对I到Max都调用F,把结果组成一个list。

函数的例子3:
sum([H|T]) -> H + sum(T);
sum([]) -> 0.

非空list匹配第一个sum,取出第一个元素H,对于list后续部分调用sum,最后返回两者的和。
到最后T为空时,返回0,全过程结束。
这样实现了求list的和。

函数的例子4:

list可以做这样的变换

L = [1,2,3,4,5].
[2*X || X <- L ].     % [2,4,6,8,10]

这样可以对list每个元素进行操作,得到新的list。

qsort([]) -> [];
qsort([Pivot|T]) ->
    qsort([X || X <- T, X < Pivot])
    ++ [Pivot] ++
    qsort([X || X <- T, X >= Pivot]).

这个例子实现快速排序。快速排序主要思想时分而治之。简单说是取数组中一个元素A,把比它小的放左边,把比它大的放右边,这样A的位置必然是正确的,然后把左右部分都这样递归操作下去,最后就能排好序。
当然这里只是一个简单的实现,现实实现要考虑各种情况、初始化数据,如何取A等。

[Pivot|T] 这个参数就把第一个元素当作了标点。其余的为T
1.[X || X <- T, X < Pivot] 把T中所有小于Pivot的元素做成一个list,并对其qsort。
2.[Pivot]
3.[X || X <- T, X >= Pivot] 把T中所有大于Pivot的元素做成一个list,并对其qsort。
把123接起来就得到了排好序的list。


case功能

就是根据匹配,分情况处理。

case Expression of
    Pattern1 -> Expr_seq1;
    Pattern2 -> Expr_seq2;
    ...
end


fall_velocity(Planemo, Distance) ->
    case Planemo of
        earth -> math:sqrt(2 * 9.8 * Distance);
        moon -> math:sqrt(2 * 1.6 * Distance);
        mars -> math:sqrt(2 * 3.71 * Distance) % no closing period!
    end.

二进制数据和位操作

<<5,10,20>>.
双尖括号包起来就是字节串。里面每个元素值必须在0-255之间。否则会被截取后8位。

按位拼字节
X = 1.
Y = 2.
Z = 233.
MM = <<X:3, Y:1, Z:2>>.
MM. % 得到 <<9:6>>

XYZ分别取最后3位1位2位再接起来得到1001即9。6bit。

FF = <<X:3, Y:1, Z:5>>.
FF. % 得到<<36,1:1>>
233 = 0b11101001,最后5位拿出来。前四位拼到前一个字节,最后剩一个1。
0b00100100,0b1

按位取字节
<<A:5, B:1, C:3>> = FF.
A得到0b00100。B得到0b1。C得到0b1
取的时候总长度要匹配,否则报错。

解析和拼装协议数据可能会非常方便。

其他复杂用法。。


Erlang进程

process是erlang的核心概念,erlang的进程是在erlang虚拟机层面实现的,不是操作系统层面的进程。可与协程类比。

shell里输入self().
返回<0.102.0>这样的数据,为当前进程的标识,即Pid。

基本概念:

process的创建和销毁非常快
process间传消息非常快
process跨平台,在所有平台表现一致
可以同时存在大量的process
process不共享内存,是完全独立的。
process间通信的唯一方式是传消息

基本操作1:

创建一个process,使用Mod模块的Func函数,参数为Args。返回Pid

Pid = spawn(Mod, Func, Args).
基本操作2:

发Message给Pid

Pid ! Message.
基本操作3:

收数据

receive
    Pattern1 ->
        Expressions1;
    Pattern2 ->
        Expressions2;
    ...
end

现在可以做一个简单的服务。

起一个server.erl

-module(server).
-author("xc").

-export([loop/0]).                            % 导出loop接口

loop()->
    io:format("loop~n"),
    receive                                   % 接收数据
        {From, {rectangle, Width, Height} }->  % 匹配长方形参数
            From ! Width * Height,            % 发送结果到From
            loop();                           % 再次开始收数据。通常这样递归会不断消耗栈空间,最后溢出。erlang有尾递归优化,不会重复消耗栈。
        {From, {circle, R} }->                 % 匹配圆形参数
            io:format("circle~n"),
            From ! 3.14159 * R * R,
            loop();
        {From, Other}->                       % 匹配其他
            From ! {error, Other},
            loop()
    after 5000 ->                             % 5秒超时会走到这里
        io:format("timeout 1~n"),
        loop()
    end.

起一个test.erl,启动server的loop,并给它发消息。

-module(test).
-author("xc").

-export([test_1/0]).                     % 导出test_1接口
-import(server, [loop/0]).               % 导入loop接口

test_1 ()->
    Pid = spawn(server, loop, []),       % 创建一个process,运行server模块的loop函数。得到Pid
    Pid ! {self(), {circle, 12} },        % 发消息给Pid。消息是{自身Pid, {circle, 12} },匹配到loop中的{From, {circle, R} }
    receive
        Result ->
            io:format("~p~n", [Result])
        after 3000->
            io:format("after 3000")
    end.

可以把某个pid和atom绑定,变的好读好找。以后直接给这个atom发消息就行了。

Pid1 = spawn(bounce,report,[1]).
register(bounce,Pid1).
bounce ! hello.

另外erlang自带一个监控页面。有当前节点的各种重要数据,包括mnesia数据库。还可以进行相关操作。
observer:start().


数据存储


-sname

monitor_node

net_adm:ping()

net_adm:world()


程序发布

rebar3是erlang常用的打包、发布工具。

www.rebar3.org
下载rebar3

https://www.rebar3.org/docs/getting-started
windows上在rebar3文件同个目录建一个rebar3.cmd

填入
@echo off
escript.exe “%~dpn0” %*

保存。再cmd运行rebar3 –help。可正常运行即可。

创建新app
rebar3 new app myapp

编译
rebar3两个文件复制进目录
rebar3 compile

创建新release
rebar3 new release myrelease

rebar3 compile


erlang版本管理

erlang otp会持续进化、发布新版本。那么如果需要测试、对比、升级的话,需要不同版本共存。

用kerl可以管理erlang版本
https://github.com/kerl/kerl


Elixr

原版erlang还是比较晦涩,有诸多不便。
Elixr在erlang基础上发展和改进。沿用erlang的vm。
把erlang的语法和otp改的更舒服。
另外扩展了多种工具、功能(模板、测试、打包发布)。


哈哈那上面erlang是不是白学了?

上次更新 2021-01-28