• 欢迎大家分享资料!前往留言板评论即可!

Erlang学习笔记/快速入门

合宙 模组资料网 3年前 (2021-05-15) 825次浏览 0个评论 扫描二维码

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目录


基本概念

  • 注释
    %号后面是注释

  • 句号
    命令结束时用英文句号.结尾。
    X = 20.

  • 变量
    大写字母或_开头的字符串表示一个变量。变量只能赋值(绑定)一次。之后不能再赋值,否则报错。
    X = 20. % 绑定了20
    如果再输X = 20.会报错

  • atom
    小写字母开头的字符串。可以看作一个标签。常用于各种匹配。

  • 多元组tuple
    P = {2, gg, {name, 3}}.
    PP = {k, P}
    可嵌套。其中gg, name, k都是atom。P, PP是tuple变量,绑定了相应的值。
    可以反向取出tuple中的值。
    {P1, P2, P3} = P. % 这样P1绑定了2,P2绑定了gg,
    P3绑定了{name, 3}

  • 列表List。跟数组一个意思,可存数字,atom,tuple,list等。
    A = [{a, 2}, 3, b]

  • 取出list中的数据
    可以用[HEAD|TAIL]的形式去匹配list。|左边的HEAD会匹配开头的一个元素,TAIL会匹配剩下的list。
    例如
    L = [1,2,3,4,5,6].
    [L1|L2] = L. % L1为1,L2为[2,3,4,5,6]。注意L2还是一个list
    [L3,L4|L5] = L. % L3为1,L4为2,L5为[3,4,5,6]。

  • 字符串
    Name = “Shagua”. % 绑定字符串
    [83,117,114,112,114,105,115,101]. % 会输出”Surprise”
    B = “信仰” % 会输出[20449,20208]
    这里会有各种转换,默认貌似是utf8编码。

  • 模式匹配
    erlang里充满了匹配。
    {X, abc} = {123, abc}. % 成功 X绑定了123
    {A, B, C} = {1, 2, “ggg”}. % 成功
    {AA, BB, CC} = {{aa, a}, bb, “cc”}. % 成功
    {AAA, BBB} = {“a”, “b”, cc}. % 失败 ”形状“不一致
    [H|T] = [1,2,3,4]. % 成功 H=1, T= [2,3,4]
    [A, B, C | T] = [a, b, c, d, e, f]. % 成功 A = a, B = b, C = c, T = [d, e, f]
    {GG, abc} = {123, gg}. % 失败 abc和gg两个atom匹配不了


模块和函数

开始用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().


数据存储

  • tuple到record
    -record(planemo, {name, gravity, diameter, distance_from_sun}).

  • map
    Planemos = #{ earth => 9.8, moon => 1.6, mars => 3.71 }.
    maps:get(moon, Planemos). % 取

  • ETS: Erlang Term Storage
    一种erlang数据表

    有四种形式
    set
    同个key只能存一个值

    ordered set
    同个key只能存一个值。key有序

    bag
    同个key可以存多个值。同个key下的值不能重复。

    duplicate bag
    同个key可以存多个值。同个key下的值可以重复。

    创建表
    T = ets:new(my_table, [ordered_set])

    插入数据
    ets:insert(T, {gg, {gt, “4wf”, <<56>>}}).

    observer:start().可看数据

    取数据
    ets:lookup(T, gg).

    删除数据
    ets:delete(TT, gg).

    删除表
    ets:delete(TT).

  • mnesia(希腊语memory)
    erlang自带的数据库。轻量快速、分布式、可同步、支持事务。
    可直接存在内存。可直接存erlang的record。
    不适用于大规模数据(虽然有成功案例),非常适合小规模应用、集群节点间维护一致的数据等。


-sname

monitor_node

net_adm:ping()

net_adm:world()


程序发布

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

http://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是不是白学了?


转载请注明原文链接:Erlang学习笔记/快速入门
喜欢 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址