Erlang基础知识学习总结

第一章 顺序编程

1.数据类型

Erlang数据类型:原子、整数、浮点数、字符串、列表(list)、元组(tuple)、记录(record)、映射(map)

如:在输入shell命令

%% 整数
1> 1.
1

%% 浮点数
2> 1.1.
1.1

%% 字符串
3> "ABC".
"ABC"

%% 原子
5> abc.
abc

%% 布尔 
6> X = true.
true

%% 列表
7> [1,2,3,4,5].
[1,2,3,4,5]

%% 元组
6> {a,"123",[1,2,3]}.
{a,"123",[1,2,3]}

原子以小写字母开头,后接一串字母、 数字、 下划线( _)或at( @)符号

原子还可以放在单引号 '内。可以用这种引号形式创建以大写字母开头(否则会被解释成变量)或包含字母数字以外字符的原子

2.简单的运算

Erlang中的加减乘除:

%% 加法
1> 1+1.
2
%% 减法
2> 2-1.
1
%% 乘法
3> 2*2.
4
%% 除法
4> 4/2.
2.0

值得注意的是,Erlang中的除法 /得到的结果是一个浮点数,如果需要得到一个整数,就需要使用div

%% 整除
5> 4 div 2.
2

在java中经常会使用 %作为取余,但是在Erlang 是不行的,只能使用 rem来做整除

%% 取余数
6> 5 rem 2.
3

3.元组

基础

元组: 如果想把一些数量固定的项目归组成单一的实体,就会使用元组( tuple)。元组会在声明它们时自动创建,不再使用时则被销毁

% 建立一个元组表示X,Y轴
P = {10,45}.

% 元组的嵌套
Persion = {persion,{name,joe},{height,1.82},{footsize,42}}.
1>{persion,{name,joe},{height,1.82},{footsize,42}}

% 如果在构建新元组时用到变量,那么新的元组会共享该变量所引用数据结构的值
2> F = {firstName,joe}.
{firstName,joe}

3> L = {lastName,tom}.
{lastName,tom}

4> P = {persion,F,L}.
{persion,{firstName,joe},{lastName,tom}}

% 元组的提取方式
̀5> Point = {point,10,45}.
{point,10,45}

6> {point,K,Z} = Point.
{point,10,45}
18> K.
10
19> Z.
45

占位符:使用下划线 _ 作为占位符,用于表示不感兴趣的那些变量。符号被称为匿名变量。与正规变量不同,同一模式里的多个_不必绑定相同的值

% 使用 _ 作为占位符提取 joe
{person,{name,joe,armstrong},{footsize,42}}
1> {_,{_,Who,_},_} = Person.
{person,{name,joe,armstrong},{footsize,42}}
2> Who. 
joe
修改元组的数据
%% 修改元组的数据,
update_tuple() ->
  List1 = {1,2,3},
  {X,Y,Z} = List1,
  List2 = {X,Y,30}.

%% 也可以使用API的形式
%% setelement(Index,Tuple,Value)
update_tuple() ->
    Tuple = {a,b,c},
    Tuple2 = setelement(1,Tuple,aa).

4.列表

定义及使用

列表( list)被用来存放任意数量的事物。创建列表的方法是用中括号把列表元素括起来,并用逗号分隔它们

% 创建一个list存储1,2,3,4,5
1> D = [1,2,3,4,5].
[1,2,3,4,5]

同时list也可以存储元组

% list中存储元组
1> Dw = [{name,tom},{height,1.3}].
[{name,tom},{height,1.3}]

也可以混合存储数字或元组

% list存储元组,数字
1> A = [{apple,14},{pear,15},15,16].
[{apple,14},{pear,15},15,16]

可以看到Erlang中的list并不是强类型的存储的,就好比Java中的list,只能存储某一类型的数据。

比如:

//创建一个list
List<Integer> javaList = new ArrayList<>();

该javaList只能存储整形的数据,而Erlang中没有这样的限制。

列表中两个概念:

列表头(head):列表的第一个元素被称为列表头

列表尾(tail) : 假设把列表头去掉,剩下的就被称为列表尾

解释:

如果有一个列表[1,2,3,4,5],那么列表头就是整数1,列表尾则是列表 [2,3,4,5]。

注意列表头可以是任何事物,但列表尾通常仍然是个列表

自定义列表

如果T是一个列表,那么[H | T]也是一个列表, 它的头是H,尾是T。竖线( | ) 把列表的头与尾分隔开。 [ ]是一个空列表 。

用法:

% 定义一个列表T
1> T = [2,3,4,5].
[2,3,4,5]
% 使用自定义列表 [1 | T]
2> T2 = [0,1 | T].
[0,1,2,3,4,5]

可以看到T2的值为[0,1,2,3,4,5]

[A,B,C|T] = [a,b,c,d,e,f] 成功: A = a, B = b, C = c, T = [d,e,f]

例子:

求[1,2,3,4,5,6,7,8,9,10]所有元素的和

%% 求和
sum([A|T]) -> A + sum(T);
sum([]) -> 0.

%% 编译
1> demo_day02:sum([1,2,3,4,5]).
15
提取列表元素

提取列表元素:可以用模式匹配操作来提取某个列表里的元素。

如果有一个非空列表L,那么表达式[X|Y] = L( X和Y都是未绑定变量)会提取列表头作为X,列表尾作为Y。

举例:有一个列表T =[2,3,4,5].

% 提取元素,X作为列表头,Y作为列表尾
1> [X|Y] = T.
[2,3,4,5]
2> X.
2
3> Y.
[3,4,5]

可以看到,这里的X就是一个元素,Y是一个列表



**代码:**提取列表中的元素; 比如[1,2,3,4,5]中提取3,如果没有就返回 -1

%% 提取列表中的元素; 比如[1,2,3,4,5]中提取3,如果没有就返回 -1
get_list(Key,[A|T]) ->
  if
    A =:= Key -> Key;
    true -> get_list(Key,T)
  end;
get_list(_,[]) -> -1.

元组和列表结合的例子:

% 定义一个T1,存储水果的报价单
1> T1 = [{oranges,4},{newspaper,1},{apples,10},{pears,6},{milk,3}].
[{oranges,4},{newspaper,1},{apples,10},{pears,6},{milk,3}]

% 提取列表头和列表尾
2> [X|Y] = T1.
[{oranges,4},{newspaper,1},{apples,10},{pears,6},{milk,3}]
3> X.
{oranges,4}
4> Y.
[{newspaper,1},{apples,10},{pears,6},{milk,3}]

这里的X、Y可以理解为变量名

列表合并

将两个列表 [1,2,3] 、[4,5,6]合并一起变成 [1,2,3,4,5,6].

%% 列表A与列表B拼接;
merge_list1([A | T], L) ->
  [A | merge_list(T, L)];
merge_list([], L) ->
  L.

%% 也可以使用API操作
merge_list2(L1,L2) ->
	lists:append(L1,L2).

%% 还可以使用++拼接两个列表
merger_list3(L1, L2) ->
	L1 ++ L2.
列表推导

列表推导( list comprehension)是无需使用fun、 map或filter就能创建列表的表达式。

有一个列表L =[1,2,3,4,5],要实现列表中的每一个元素平方

%% 原来的方式
1> lists:map(fun(M) -> M*M end,L).
[1,4,9,16,25]

%% 列表推导
2> [X*X || X <-L].
[1,4,9,16,25]

在列表推导的式子中,X来源于 列表L中

实现快速排序:

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

5.模式匹配

Erlang中的 =并不像我们之前的语言是赋值的意思,这里是模式匹配的意思;

img

可以看到对应的参数都进行了参数上的绑定。

习题: 假定 X = 1,对于以下模式匹配,请说明是正确还是错误,并说明为什么;

X = 1.0.   			   %%错误,1.0为浮点数
X = X * 1. 			   %% 正确
{X, abc} = {abc, abc}. %% 错误,X已经模式匹配为1了,此时不能再匹配为abc
[X | _] = [1].		   %% 正确,列表头为1
[a | _] = "abc". 	   %% 错误,a为原子,“abc”字符编码可以转换为[97,98,99],但a并没有转为字符编码
                       %% [97 | _ ] = “abc".则正确

[A, 97 | _] = "abc".   %% 错误;“abc”字符编码可以转换为[97,98,99],此时模式匹配不上
[X | _] = "1bc".	   %% 错误;1的字符编码为 49,X已绑定值1
[A, B | _] = [a, b, c, d]. %% 正确,A匹配为a,B匹配为b
{A, B} = {a, b, c}.    %% 错误,数量不匹配

6.函数

  1. 编写两个函数,实现要求如下:
    • 函数1:传入整数 X,当X1时返回true,其余情况返回false
find(1) -> true;
find(_) -> false.
    • 函数2:传入整数 X ,经过公式 X / 1 后值为1时返回true, 其余情况返回false
find(X) when X / 1 =:= 1 -> true;
find(_) -> false.

注意:

同名函数不能参数数量不一致:

错误写法:
img

修改:把一个参数的函数名使用.结束

img

lists类库

lists:map

lists:map(F,L)。这个函数返回的是一个列表,它通过给列表L里的各个元素应用fun F生成;

这里的F指的是一个具体操作的函数,L是列表

如给一个列表的每一个元素都相乘:

1> lists:map(fun(X) -> X*X end,[1,2,3,4,5]).
[1,4,9,16,25]

这里的X是指从列表[1,2,3,4,5]中获取的元素

lists:filter:

lists:filter(F, L),它返回一个新的列表,内含L中所有符合条件的元素

如:给定一个列表,返回能被2整除的数

1> lists:filter(fun(X) -> X rem 2 =:= 0 end,[1,2,3,4,5]).
[2,4]

类似的功能实现:

lists:member:

lists:member(X, L):如果X是L中的成员,就返回true,否则返回false。

判断1是否在列表[1,2,3,4,5]中

1> lists:member(1,[1,2,3,4]).
true

lists:append:

lists:append(L1,L2):合并两个列表

%% 合并两个列表
1> lists:append([1,2,3,4],[5,6,7,8]).
[1,2,3,4,5,6,7,8]

lists:seq:

lists:seq(Start,End).返回一个从Start~End的列表

%% 返回从1 到5的列表
4> lists:seq(1,5).
[1,2,3,4,5]
列表增删改查

列表除了使用自定义列表、递归等形式操作,还能使用其内部函数lists提供的API操作

Erlang_API中文手册:http://erldoc.com/

前置条件:定义名为(role)的record,包含id、name、sex、lev、age、score字段;

定义role实例,存储到list中并实现以下功能:

增删改查role实例;

%% 定义一个实例
1> R1 = #role{id=1,name=r1,lev=1,age=18,score=60}.
#role{id = 1,name = r1,sex = undefined,lev = 1,age = 18,score = 60}

%% 存储在list中
2> L1 = [RoleList].
[#role{id = 1,name = r1,sex = undefined,lev = 1,age = 18,score = 60}]

%% 增加
3> L2 = [R1 | L1].

%% 删除
4> L3 = lists:delete(R1,L2).
[#role{id = 1,name = r1,sex = undefined,lev = 1,age = 18,score = 60}]

%% 查找   ->这里是指查找id为1的数据
5> lists:keyfind(1,2,L3).
#role{id = 1,name = r1,sex = undefined,lev = 1,age = 18,score = 60}

%% 更新,将id为1的更新为记录更新为R2
6> R2 = R1#role{id=2,name=r2}.
#role{id = 2,name = r2,sex = undefined,lev = 1,age = 18,score = 60}

7> L4 = lists:keyreplace(1,2,L3,R2).
[#role{id = 2,name = r2,sex = undefined,lev = 1,age = 18,score = 60}]

keyfind(Key, N, TupleList) :在一个元组列表里查找一个元素

Key是指需要查找的值,N是指位置

如这里:指查找id为1的数据

%% 查找
5> lists:keyfind(1,2,L3).
#role{id = 1,name = r1,sex = undefined,lev = 1,age = 18,score = 60}

%% 查找name = r1的值 ,此处3是指位置:role,id,name,sex,lev,age,socre,name排第3位
5> lists:keyfind(r1,3,L3).

  • 根据指定键值排序打印list;
%% 根据id从大到小排序
8> lists:sort(fun(A,B) -> 
                   case (A#role.id > B#role.id) of 
                       true -> true;
                       false ->false 
                   end 
               end,L2).
[#role{id = 2,name = r2,sex = undefined,lev = 1,age = 19,score = 61},
 #role{id = 1,name = r1,sex = undefined,lev = 1,age = 20,score = 60}]

	
%% 或者直接使用api
lists:keysort(2,L2)

函数返回

函数的嵌套使用

1> F1 = fun(L) -> ( fun(X) -> lists:member(X,L) end) end. 
#Fun<erl_eval.6.118419387>
2> F2 = F1([1,2,3,4,5]).
#Fun<erl_eval.6.118419387>
3> F2(1).


内置函数

list_to_tuple/1 能将一个列表转换成元组

%% 将list转变为tuple
1> list_to_tuple([a,b,c,d]).
{a,b,c,d}

f() 能够清除 erlang Shell 原有绑定

1> f().
ok

time()显示时分秒

6> time().
{14,57,31}

实现for循环

%% 实现一个for循环,生成一个1,10的列表
for(Max,Max,F) -> [F(Max)];
for(I,Max,F) -> [I| for(I+1,Max,F)].

%% 编译测试
1> demo_day02:for(1,10,fun(X) -> X end).
[1,2,3,4,5,6,7,8,9,10]

7.关卡

关卡( guard)是一种结构,可以用它来增加模式匹配的威力。
使用when语句比较X,Y的大小

%% 比较X、Y的大小
max(X,Y) when X > Y -> X;
max(X,Y) -> Y.

关卡里分隔各个条件的逗号(,)的意思是“并且” 的意思

%% 当X>Y且X>10的时候
max(X,Y) when X>Y,X>10 -> X.

关卡里分号( ;)的意思是“或者” 的意思

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

%% 使用高阶函数的形式将list分为原子和整数
split(List) ->
	F = fun(I, {IntegerList, AtomList}) when is_integer(I) -> 
            {[I | IntegerList], AtomList};
		(Atom, {IntegerList, AtomList}) when is_atom(Atom) -> 
            {IntegerList, [Atom | AtomList]};
		(_, {IntegerList, AtomList}) -> 
            {IntegerList, AtomList}
		end,
	lists:foldl(F, {[], []}, List).

%% 测试
1> demo:split([1,2,e,5,4,3,a,b,d]).
{[3,4,5,2,1],[d,b,a,e]}

8. If and case

%% 使用if来判断大小
max_if(X,Y) ->
  if
    X > Y -> X;
    true -> Y
  end.

%% 使用case of的形式
max_case_of(X,Y) ->
  case X>Y of
    true -> X;
    false -> Y
  end.

9.记录和映射

记录Record

记录是元组的另一种形式,本质上是元组 ,记录的存储与性能特性和元组一样。

应该在下列情形里使用记录:

  • 用一些预先确定且数量固定的原子来表示数据时;
  • 记录里的元素数量和元素名称是不改变的

列表的定义和操作:records.hrl

%% 定义一个记录
-record(todo,{status=1,who=joe,text}).

%% 编译文件
1> rr("records.hrl").
[todo]

%% 创建新的记录
2> #todo{}.
#todo{status = 1,who = joe,text = undefined}

%% 模式匹配给X1,并更新who、text字段的值
3> X1 = #todo{status=1,who=x1,text=hello}.
#todo{status = 1,who = x1,text = hello}

%% 在第4行复制了X1记录并修改字段status的值为2
4> X2 = X1#todo{status=2}.
#todo{status = 2,who = x1,text = hello}

提取记录字段:

要在一次操作中提取记录的多个字段,可以使用模式匹配

%% 提取X2中的who和text的值
5> #todo{who=W,text=T} = X2.
#todo{status = 2,who = x1,text = hello}
6> W.
x1
7> T.
hello
8>

如果只是想要记录里的单个字段,就可以使用“点语法”来提取该字段。

%% 提取X2的单个字段
9> X2#todo.text.
hello

映射组Map

映射组适合以下的情形:

  • 当键不能预先知道时用来表示键值数据结构;

  • 当存在大量不同的键时用来表示数据;

  • 用来表示键值解析树,例如XML或配置文件;

  • 用JSON来和其他编程语言通信

映射组的定义:

%% 定义一个映射组
1> F1 = #{a =>1,b =>2}.
#{a => 1,b => 2}

%% 也可以创建一个非原子键的映射组
2> F2 = #{{wife,fred} =>"sue",{age,fred} => 45}.
#{{age,fred} => 45,{wife,fred} => "sue"}

映射组的更新:使用=>或 =:

%% 使用 =>新增一个数据
3> F3 = F1#{c => 3}.
#{a => 1,b => 2,c => 3}

%% 使用 =:修改数据
4> F4 = F3#{a:=11}.
#{a => 11,b => 2,c => 3}

表达式 =>

  • 将现有键K的值更新为新值V
  • 另一种是给映射组添加一个全新的键值对。这个操作总是成功的

表达式K :=

  • 是将现有键K的值更新为新值V。 如果被更新的映射组不包含键K,这个操作就会失败

常用api:

maps:new()

返回一个新的空映射组

is_map(M)

判断是不是一个映射组

maps:to_list(M)

把映射组M里的所有键和值转换成一个键值列表。键在生成的列表里严格按升序排列

%% 将一个map转为list
1> F1 = #{a => 1,b => 2,c => 3}.
#{a => 1,b => 2,c => 3}
2> maps:to_list(F1).
[{a,1},{b,2},{c,3}]

maps:map_size(Map)

返回映射组里的条目数量

maps:is_key(Key,Map)

如果映射组Map包含Key就返回true,否则就返回false

%% 判断某个key在不在映射组里
1> F1 = #{a => 1,b => 2,c => 3}.
#{a => 1,b => 2,c => 3}
2> maps:is_key(a,F1).
true

maps:get(Key,Map)

从Map中取出Key的值,如果没有就抛出异常错误

1> F1 = #{a => 1,b => 2,c => 3}.
#{a => 1,b => 2,c => 3}
2> maps:get(a,F1).
1

maps:find(key,Map)

从Map中取出Key的值,如果没有就返回error

1> F1 = #{a => 1,b => 2,c => 3}.
#{a => 1,b => 2,c => 3}
2> maps:find(a,F1).
{ok,1}

maps:keys(Map)

返回Map中所有的Key

maps:remove(c,F1).

返回一个新映射组M1,除了键为Key的项(如果有的话)被移除外,其他与M一致

映射组的增删改查

前置条件:定义名为(role)的record,包含id、name、sex、lev、age、score字段;

定义多个role实例,存储到map中,id作为键,role实例作为值:

2> R1 = #role{id=1,name=r1,sex=1,lev=1,age=18,score=61}.
#role{id = 1,name = r1,sex = 1,lev = 1,age = 18,score = 61}
3> R2 = R1#role{id=2,name=r2}.
#role{id = 2,name = r2,sex = 1,lev = 1,age = 18,score = 61}
4> R3 = R1#role{id=3,name=r3}.
#role{id = 3,name = r3,sex = 1,lev = 1,age = 18,score = 61}

  • 增删改查role实例;
%5 增加实例
5> M1 = maps:put(R1#role.id,R1,Map).
#{1 =>#role{id = 1,name = r1,sex = 1,lev = 1,age = 18,score = 61}}
6> M2 = maps:put(R2#role.id,R2,M1).
#{2 =>#role{id = 2,name = r2,sex = 1,lev = 1,age = 18,score = 61}}
7> M3 =  maps:put(R3#role.id,R3,M2).
#{3 =>#role{id = 3,name = r3,sex = 1,lev = 1,age = 18,score = 61}}


%% 删除一个R3实例
8> M4 = maps:remove(R3#role.id,M3).
#{1 =>#role{id = 1,name = r1,sex = 1,lev = 1,age = 18,score = 61},
  2 =>#role{id = 2,name = r2,sex = 1,lev = 1,age = 18,score = 61}}

%% 修改一个实例
9> M5 = maps:update(R2#role.id,R1#role{name=update},M4).
#{1 =>#role{id = 1,name = r1,sex = 1,lev = 1,age = 18,score = 61},
  2 => #role{id = 1,name = update,sex = 1,lev = 1,age = 18,score = 61}}

%% 查找R1实例
10> M6 = maps:get(R1#role.id,M5).
#role{id = 1,name = r1,sex = 1,lev = 1,age = 18,score = 61}

10.递归和尾递归

普通递归求和一个列表[1,2,3,4,5]

%% 求和
sum([A|T]) -> A + sum(T);
sum([]) -> 0.

%% 编译
1> demo_day02:sum([1,2,3,4,5]).
15

尾递归优化:

tail_sum([A | T]) ->
	tail_sum(A, T).
tail_sum(N, [A | T]) ->
	tail_sum(N + A, T);
tail_sum(N, []) ->
	N.

尾递归求和一个数字N

%% 尾递归求和1~N
tail_sum2(N) ->
	tail_sum2(N, 1).
tail_sum2(1, Result) ->
	Result;
tail_sum2(N, Result) ->
	tail_sum2(N - 1, Result + N).
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值