返回目录
启动 Erlang Shell
erl
参数 | 说明 |
---|---|
-noshell | 不显示 Erlang Shell,执行任务时使用 |
windows系统
erl %% 可以使用重定向标准输入输出 或 管道
werl %% 支持编辑快捷键,显示正确的编码字符
全局函数/命令
命令 | 说明 |
---|---|
help(). | 打印可用的shell函数 |
h(). | 打印先前输入过的命令 |
v(N). | 取出第N号提示符对应的计算结果 |
f() | 让 shell 忘记现有的任何绑定 |
cd(Dir). | 更改当前目录(Dir是双引号字符串) |
ls(). | 打印目录内容 |
pwd(). | 打印当前目录路径 |
i(). | 打印当前系统的运行时信息 |
memory(). | 打印内存使用信息 |
q(). init:stop(). | 有序的方式关停,并退出 Erlang Shel |
halt(). erlang:halt(). | 立即退出 Erlang Shel |
Erlang Shell 里不能输入附注("-"开头的语句)
快捷键
命令 | 说明 |
---|---|
Ctrl + C | 调出BREAK菜单 |
Ctrl + G | 调出用户开关命令菜单, 输入h 显示可用命令 回复控制:i 回车 c 回车 |
Ctrl + A | 行首 |
Ctrl + D | 删除当前字符 |
Ctrl + E | 行尾 |
Ctrl + F 或→ | 向前的字符 |
Ctrl + B 或← | 向后的字符 |
Ctrl + P 或 ↑ | 前一行 |
Ctrl + N 或 ↓ | 下一行 |
Ctrl + T | 调换最近两个字符的位置 |
Tab | 尝试扩展当前模块或函数的名称 |
返回目录
操作符 | 说明 |
---|---|
% | 行注释 |
() | 优先运算符 |
= | 赋值, 匹配 |
, | 元素分隔符 |
; | 句子分隔符 |
. | 语句结束符 |
返回目录
操作符 | 描述 | 参数类型 | 优先级 |
---|---|---|---|
+ X | 正 | 数字 | 1 |
- X | 负 | 数字 | 1 |
X * Y | 乘 | 数字 | 2 |
X / Y | 浮点除 | 数字 | 2 |
bnot X | 按位取反 | 整数 | 2 |
X div Y | 整除 | 整数 | 2 |
X rem Y | 取余 | 整数 | 2 |
X band Y | 按位与 | 整数 | 2 |
X + Y | 加 | 数字 | 3 |
X - Y | 减 | 数字 | 3 |
X bor Y | 按位或 | 整数 | 3 |
X bxor Y | 按位异或 | 整数 | 3 |
X bsl N | 把 X 向左算术位移 N 位 | 整数 | 3 |
X bsr N | 把 X 向右算术位移 N 位 | 整数 | 3 |
返回目录
操作符 | 说明 |
---|---|
not B1 | 逻辑非 |
B1 and B2 | 逻辑与 |
B1 andalso B2 | 短路逻辑与 |
B1 or B2 | 逻辑或 |
B1 orelse B2 | 短路逻辑或 |
B1 xor B2 | 逻辑异或 |
B1和B2都必须是布尔值或者执行结果为布尔值的表达式
% 当 X 为 0 时,f(X) 里的关卡会失败,但 g(X) 里的关卡会成功
f(X) when (X == 0) or (1/X > 2) -> ...
g(X) when (X == 0) orelse (1/X > 2) -> ...
返回目录
允许不同变量进行比较操作
操作符 | 说明 |
---|---|
< =< | 小于 小于等于 |
> >= | 大于 大于等于 |
=:= == | 等于(区分整型和浮点型) 等于(不区分数字类型) |
=/= /= | 不等于(区分整型和浮点型) 不等于(不区分数字类型) |
数据类型之间的大小顺序:
number < atom < reference < fun < port < pid < tuple < list < bit string
返回目录
1> One. %% 使用未绑定的变量,报错
* 1: variable 'One' is unbound
2> One = 1. %% 变量 One 的值为 1
1
3> Un = Uno = One = 1. %% 变量Un、Uno、One 的值为 1
1
4> Two = One + One. %% 变量 Two 的值为 2
2
5> Two = 2. %% 变量 Two 的值为 2
2
6> Two = Two + 1. %% 变量 Two 的值 为 3,报错,值不能改变
** exception error: no match of right hand side value 3
返回目录
用模式匹配来代替赋值操作
模式 = 单元 | 结果 |
---|---|
_ | 能重复匹配任何数据 |
{X, abc} = {123, abc}. | 成功:X = 123 |
{X, Y, Z} = {222, def, “cat”}. | 成功:X = 222, Y = def, Z = “cat” |
{X, Y} = {333, ghi, “cat”}. | 失败:元组的结构不同 |
X = true. | 成功:X = true |
{X, Y, X} = {{abc, 12}, 42, {abc, 12}}. | 成功:X = {abc, 12} Y = 42 |
{X, Y, X} = {{abc, 12}, 42, true}. | 失败:X的值不同({abc, 12} 与 true 不同) |
[H | T] = [1, 2, 3, 4, 5]. | 成功:H = 1, T = [2, 3, 4, 5] |
[H | T] = “cat”. | 成功:H = 99, T = “at”] |
[A, B, C | T] = [a, b, c, d, e, f] | 成功:A = a, B = b, C = c, T = [d, e, f] |
返回目录
基础数据类型:
类型 | 说明 | 备注 |
---|---|---|
Integer | 整型 | |
Float | 浮点型 | |
Atom | 原子型 | |
bool | 布尔型 | |
Tuple | 元组型 | |
List | 列表 | |
String | 字符串 | |
fun | 函数型 | |
Union | ||
binary | 二进制型 | |
bitstring | 二进制串 | |
record | 记录 | |
map | 映射组 |
内部类型:
类型 | 说明 | 备注 |
---|---|---|
any() | ||
none() | ||
pid() | 进程id | |
port() | 端口 | |
reference() | ||
[] | ||
Atom | ||
binary() | ||
float() | ||
Fun | ||
Integer | ||
Tuple | ||
Union | ||
UserDefined | ||
ref |
返回目录
1> N10 = 15. %% 十进制
1> N2 = 2#00101010. %% 二进制
2> N8 = 8#123756322. %% 八进制
3> N16 = 16#af6bfa23. %% 十六进制
1> $a. %% 字符 a 的ASCII 编码是 97
2> $1. %% 字符 1 的ASCII 编码是 49
3> $n. %% 换行符 的ASCII 编码是 10
4> $^c. %% Ctrl+C 的ASCII 编码是 3
返回目录
采用 IEEE 754-1985 格式的 64 位双精度浮点数。可以表示绝对值在 10-323 到 10308 范围内的实数。由五部分组成:
1> F1 = 1.0.
2> F2 = 3.1415926.
3> F3 = -2.3e+6.
4> F4 = 23.56E-27.
返回目录
1> cat.
cat
2> 'Cat'.
'Cat'
3> cat =:= 'cat'.
true
返回目录
Erlang 中没有布尔型,true 和 false 都是原子
返回目录
元组会在定义时自动创建,不再使用时则被销毁
1> P1 = {10, 45}. %% 声明元组
2> P2 = {point, 10, 45}. %% 增加可读性
3> P3 = {person, {name, joe}, {height, 1.82}}. %% 元组嵌套
1> {X, Y} = {10, 45}. %% X为10,Y为45
2> {point, X, Y} = {point, 10, 45}. %% X为10,Y为45
3> {point, C, C} = {point, 25, 25}. %% C为25,如值不同则报错
4> {_, {_, Who}, _} = P3. %% Who为joe。_为通配符
返回目录
列表的第一个元素叫表头(head),其余的叫表尾(tail)。注意列表头可以是任何事物,但列表尾通常仍然是个列表。
举例:T是个列表,[H|T]也是个列表,头是H,尾是T。竖线(|)把头与尾隔开。[]是个空列表。
访问列表头是一种非常高效的操作,先处理表头,再处理表尾。
定义列表
1> Drawing = [{square,{10,10},10}, {triang,{15,10},{25,10},{30,40}}].
2> ThingsToBuy = [{apple,10}, {pears,6}, {milk,3}].
%% 多表头
3> ThingsToBuy1 = [{oranges,4}, {newspaper,1} | ThingsToBuy].
提取列表的元素
1> [Buy1 | ThingsToBuy2] = ThingsToBuy1.
%% Buy1 = {oranges, 4}
%% ThingsToBuy2 = [{newspaper,1}, {apples,10}, {pears,6}, {milk,3}]%% 多表头
2> [Buy2, Buy3 | ThingsToBuy3] = ThingsToBuy2.
%% Buy2 = {newspaper,1}
%% Buy3 = {apples,10}
%% ThingsToBuy3 = [{pears,6}, {milk,3}]
如果有一个非空列表 L,那么表达式 [X | Y] = L (X, Y都是未绑定变量)会提取列表头作为X,列表尾作为Y。
列表元素操作
++ 可以把列表粘合起来,-- 可以删除列表中的元素。
1> [1, 2, 3] ++ [4, 5].
[1, 2, 3, 4, 5]
2> [1, 2, 3, 4, 5] -- [1, 2, 3].
[4, 5]
3> [2, 4, 2] -- [2, 4].
[2]
两个操作符都是右结合的。所以都要从右向左进行操作。
1> [1, 2, 3] -- [1, 2] -- [3].
[3]
2> [1, 2, 3] -- [1, 2] -- [2].
[2, 3]
数据量大时不推荐使用,效率低
返回目录
列表推导(list comprehension)是无需使用fun、map或filter就能创建列表的表达式。它让 程序变得更短,更容易理解。
构造自然顺序的列表
- 总是向列表头添加元素。
- 从输入列表的头部提取元素,然后添加到输出列表的头部,形成的结果与输入列表顺序相反的输出列表。
- 如果顺序很重要,调用lists:reverse/1函数(高度优化过)
- 避免违反以上的建议。
普通形式 [ expression || Pattern <- ListExpr ]
英文 | 中文 | 说明 |
---|---|---|
expression | 表达式 | 用于生成新的元素 |
Pattern | 模式 | 用于匹配 list 中的各个元素 |
ListExpr | 列表 | 用生成新列表的源列表 |
示例:
1> [ 2*X || X <- [1,2,3,4,5] ].
[2,4,6,8,10]2> map(F,L) -> [F(X) || X <- L].
%% 等价于 map(F, [H | T]) -> [F(H) | map(F, T)].
从 [1,2,3,4,5] 中按顺序挨个取出一个元素匹配给 X,然后用 2*X 计算出新列表相应顺序的元素值。[2,4,6,8,10]
常见形式 [ X || Qualifier1, Qualifier2, …]
英文 | 中文 | 说明 |
---|---|---|
X | 表达式 | 任意一条表达式 |
Qualifier | 限定符 | 可以是 generator 或 bitstring、和 filter |
带过滤器的示例:
1> [X || X <- [1,2,3,4], X rem 2 =:= 0].
[2, 4]
Qualifier
英文 | 中文 | 形式 | 说明 |
---|---|---|---|
generator | 生成器 | Pattern <- ListExpr | ListExpr:必须是能得出列表的表达式 |
bitstring | 位串生成器 | BitStringPattern <= BitStringExpr | BitStringExpr 必须是能得出 bitstring 的表达式 |
filter | 过滤器 | 既可以是判断函数(返回true或false), | 列表推导里的生成器部分起着过滤器的作用 |
多个生成器的示例:
1> [X+Y || X <- [1, 2], Y <- [3, 4]].
[4, 5, 5, 6] %% 会执行 1+3、1+4、2+3、2+4 的操作
生成器启动过滤的作用的示例:
1> [X || {a, X}<-[{a,1}, {b,2}, {c,3}, {a,4}, hello, "wow"]].
[1,4]
返回目录
二进制型节省空间,用来保存大量的原始数据(大型字符串、文件内容)
<<E1, E2, ..., En>>
1> B1 = <<5, 10, 20>>. %% 整数(0~255)
<<5,10,20>>
2> B2 = <<"hello">>. %% 字符串
<<"hello">>
3> B3 = <<65, $B, 67>>. %% 内容是的 ASCII 码
<<"ABC">>
返回目录
用来定义二进制型和位串
格式:<<E1, E2, ..., En>>
Ei为其中某一元素。
Ei = Value |
Value:Size |
Value/TypeSpecifierList |
Value:Size/TypeSpecifierList
Value 值
范围 | 类型 | 示例 |
---|---|---|
定义时 | 字符串 整数(0~255) 浮点数 二进制型 已绑定变量 表达式 | <<“example”>> <<11, 22, 33>> <<>> <<>> A = 12. <<A, A>>. <<>> |
模式匹配 | 未绑定变量 字符串 整数(0~255) 浮点数 二进制型 已绑定变量 | <<X1, X2>> = <<-44>> <<>> <<>> <<>> <<>> <<>> |
Size 所占位数
范围 | 类型 | 示例 |
---|---|---|
定义时 | 整数 表达式 | |
模式匹配 | 整数 已绑定变量 |
Size 的默认值会根据 Value 的类型变化。在模式匹配里,默认值只对最后那个元素有效。
类型 | 默认值 |
---|---|
整数 | 8 |
浮点数 | 64 |
二进制型 | 是其大小 |
TypeSpecifierList 类型指定列表
形式:End-Sign-Type-Unit;顺序任意,都可省略(为默认值)
形式 | 描述 | 说明 |
---|---|---|
End | 字节序列 | big:高位优先,网络字节顺序(network byte order)。默认值 little:低位优先 native:在运行时根据机器的 CPU 来确定 |
Sign | 符号类型 | unsigned 无符号,默认值 signed:有符号 只用于模式匹配 |
Type | 数据类型 | integer,默认值 float binary | bytes bitstring | bits utf8 utf16 utf32 |
Unit | 数据单位 | 写法:unit:1|2|…256 integer、float、bitstring 的默认值是1 binary 的默认值为 8 utf8、utf16、utf32 类型无需定义 |
这个值的所占空间 = Size * Unit
待整理的示例:
%% 存储24位真彩色
1> Color = 16#F09A29.
157681052> pixel = <<Color:24>>.
<<240, 154, 41>>3> Pixels = <<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>>.
<<213,45,132,64,76,32,76,0,0,234,32,15>>4> <<Pix1:24, Pix2:24, Pix3:24, Pix4:24>> = Pixels.
<<213,45,132,64,76,32,76,0,0,234,32,15>>5> <<R:8, G:8, B:8>> = <<Pix1:24>>.
<<213,45,132>>6> R.
2137> <<R1:8, Rest/binary>> = Pixels.
<<213,45,132,64,76,32,76,0,0,234,32,15>>8> R1.
213%% 获取 Size
<<Size:4, Data:Size/binary>> = <<1,2,3,4,5,6>>M = <<X:3, Y:7, Z:6>> %M的类型是binary 因为数据的总长度是16位,可以被8整除
M = <<X:2, Y:7, Z:6>> %M的类型是bitstring 因为总位数是151> Red = 2.
2> Green = 61.
3> Blue = 20.
% 打包颜色, 定义一个16位数据的双字节二进制型
4> Mem = <<Red:5, Green:6, Blue:5>>.
<<23,180>>
% 解包颜色
5> <<R1:5, G1:6, B1:5>> = Mem.
返回目录
位串(bitstring)的模式匹配是位级操作,这样我们就能在单次操作里打包和解包 位的序列。
这对编写需要操作位级数据的代码来说极其有用,例如没有按照8位边界对齐的数据或者可变长度数据,它们的数据长度用位而不是字节来表示。
1> B1 = <<1:8>>.
<<1>>
2> byte_size(B1).
1
3> is_binary(B1).
true
4> is_bitstring(B1).
true5> B2 = <<1: 17>>.
<<0,0,1:1>>
6> is_binary(B2).
false
7> is_bitstring(B2).
true
8> byte_size(B2).
3 %% 字节大小
9> bit_size(B2).
17 %% 位大小
因为文件和套接字使用的单位是字节,所以不能把一个位串写入文件或套接字。
返回目录
提取出字节里各个单独的位,
遍历二进制型并生成列表或二进制型。
1> B = <<16#5f>>.
<<"_">> %% 包含单个字节的二进制型
2> [X || <<X:1>> <= B].
[0,1,0,1,1,1,1,1] %% 包含字节里各个位的列表
3> << <<X>> || <<X:1>> <= B >>.
<<0,1,0,1,1,1,1,1>> %% 包含这些位的二进制型
返回目录
Erlang 用列表和二进制型表示字符串。
定义字符串
1> Name = "Kate".
"Kate"
在 shell 中,如果列表内的所有整数都代表可打印字符(ASCII 码),那么就会将其打印成字符串字面量,否则,打印成列表。
2> [83, 117, 114, 112, 114, 105, 115, 101].
"Surprise"3> [1, 83, 117, 114, 112, 114, 105, 115, 101].
[1,83,117,114,112,114,105,115,101]
用 $char 格式输出字符的 ASCII 码
1> $a.
97
2> $1.
49
3> $n.
10
4> $^c.
3
5> I = $s.
115
6> [I-32, $u, $r, $p, $r, $i, $s, $e].
"Surprise"
输出数字或 Unicode 字符
1> X = [97,98,99].
"abc"
2> io:format("~w~n",["abc"]).
[97,98,99]%% 输出 Unicode字符
3> X1 = "ax{221e}b".
[97,8734,98]
4> io:format("~ts~n", [X1]).
a∞b
字符集
从 Erlang R16B 开始,Erlang 的源代码文件都假定采用UTF-8 字符集编码。在这之前用的是 ISO-8859-1(Latin-1)字符集。这意味着所有 UTF-8 可打印字符都能在源代码文件里使用,无需使用任何转义。
Erlang内部没有字符数据类型。字符串其实并不存在,而是由整数列表来表示。用整数列表表示 Unicode 字符串是毫无问题的。
转义字符
转义字符 | 含义 | 整数编码 |
---|---|---|
b | 退格符 | 8 |
d | 删除符 | 127 |
e | 换行符 | 27 |
f | 换页符 | 12 |
n | 换行符 | 10 |
r | 回车符 | 13 |
s | 空格符 | 32 |
t | 水平制表符 | 9 |
v | 垂直制表符 | 11 |
x{…} | 十六进制字符 | |
^a…^z 或 ^A…^Z | Ctrl+A 至 Ctrl+Z | 1 至 36 |
’ | 单引号 | 39 |
" | 双引号 | 34 |
\ | 反斜杠 | 92 |
C | C的 ASCII编码(C是一个字符) | (一个整数) |
各种字符串的区别
表现方式 | 占用空间 | 匹配/操作 | 比较 |
---|---|---|---|
列表字符串 | 多 | 简单 | 线性时间 |
二进制字符串 | 少 | 复杂 | 线性时间 |
原子 | 有上限 | 只能比较 | 常量时间(快) |
返回目录
-Name(Attribute).
的形式。-module(Name).
模块名:必须定义的第一条属性,其名称必须与文件名相同,首字母小写,是一个原子(atom)。-author(Name).
模块的作者。-vsn(VersionNumber).
模块版本号,默认自动生成创建模块文件 l
-module(geometry). %% 定义模块
-export([area/1]). %% 导出方法
-import(other, [fn1/1,fn2/2]). %% 导入模块 other 的方法 fn1 和 fn2;%% /n 表示有n个参数%% 定义方法:计算长方形的面积
%% 首先该方法进行匹配:使得 Width=10、Height=5
%% 然后再执行 -> 后的代码 Width * Height; 计算得 50
area({rectangle, Width, Height}) ->Width * Height;%% 方法重载,用分号分隔
area({circle, Radius}) ->3.14159 * Radius * Radius;%% 结束用句号结尾
area({square, Side}) ->Side * Side.
第一行 -module(geometry).
为声明 geometry 模块。
第二行 -export([area/1]).
为导出方法,area 为方法名,/1 表示该方法有一个参数。
第三行 -import(other, [fn1/1,fn2/2]).
为导入 other 模块中的方法 fn1/1 和 fn2/2。
在 Erlang Shell 中编译并执行模块代码
用内置函数 c/1
对 l 进行编译。并生成 geometry.beam 文件
%% 1. 编译模块
1> c(geometry).
{ok,geometry}%% 2. 带调试信息的编译模块
%% debug_info 开启调试信息,建议一直开启
%% export_all 导出模块内所有方法
2> c(geometry, [debug_info, export_all]).%% 2.1 也可以在模块内添加属性达到同样的效果
%% -compile([debug_info, export_all]).
不进入 Erlang Shell,直接编译模块
l
编译成本地代码(需平台的支持),传闻快20%,但是 .beam 文件不能跨平台使用。
hipe:c(Module, OptionsList).
# 或在 Erlang Shell 中执行:
1> c(Module, [native]).
调用模块中的方法:Module:Function(Arguments)
2> geometry:area({rectangle, 10, 5}).
50
测试代码(生产环境还是用功能全的测试代码)
-module(geometry).
-export([area/1]).test() ->12 = area({rectangle, 3, 4}),144 = area({square, 12}),tests_wored.area({rectangle, Width, Height}) -> Width * Height;
area({square, Side}) -> Side * Side.
返回目录
-define(MACRO, some_value).
%% 常量宏
-define(HOUR, 3600). %单位是秒
%% 函数宏
-define(sub(X,Y), X-Y). %两个数相减
c(MODULE, [{d, Macro, Value},...])
Value 可以省略1> c(main, [{d, 'DEBUGMODE'}]).
?MACRO
%% Erlang 内置的宏
?MODULE % 模块名
?FILE % 文件名
?LINE % 当前行号
-ifdef(MACRO).
、-else.
、endif.
-ifdef(DEBUGMODE).
-define(DEBUG(S), io:format("dbg:"++S)).
-else.
-define(DEBUG(S), ok).
-endif
返回目录
模块属性是描述模块自身的元数据。
Module:module_info().
%% 编译模块
1> c(main).%% 查看模块 main 的信息。
2> main:moudle_info().
[{module,main},
{exports,[{run,3},{sum,1},{map,2},{module_info,0},{module_info,1}]},
{attributes,[{vsn,[215390944566671778967536641503511308425]}]},
{compile,[{version,"7.5.4"},{options,[native]},{source,"/Users/lixu/example/erl_l"}]},
{native,true},
{md5,<<119,213,217,218,169,99,126,49,30,250,134,13,165,26,146,174>>}]
Module:module_info/1
.3> mian:module_info(attributes).
[{vsn,[215390944566671778967536641503511308425]}]
返回目录
返回目录
定义函数
在 模块 里定义函数
hypot(X, Y) -> %% 定义 命名函数 math:sqrt(X*X + Y*Y). %% 最后一句是函数的返回值
在 模块 和 shell 里定义匿名函数
1> Double = fun(X) -> 2*X end. %% 定义 匿名函数
2> Double(5). %% 调用 匿名函数
函数中的语句用逗号 , 分隔,用 . 结尾
函数重载
在 Erlang Shell 中定义重载函数(参数个数相同,值不同)
%% 转换华氏度与摄氏度
1> TempConvert = fun({c, C}) -> {f, 32+ C*9/5};
1> ({f, F}) -> {c, (F-32)*5/9} %% 省略了fun
1> end.
#Fun<erl_eval.7.126501267>2> TempConvert({c, 100}).
{f,212.0}3> TempConvert({f, 212}).
{c,100.0}
在 模块 中定义重载函数
%% 导出模块内的方法(函数)
-export([abc/1, abc/2]).abc({sum, A, B}) -> A * B.
abc({total, A, B}) -> A + B. %% 参数个数相同
abc({sum, A, B}, C) -> A + B + C. %% 参数个数不同
定义模块的方法:用分号 ; 间隔同名函数的不同实现。最后以 . 结尾。
函数中没有显式的返回语句,用最后一句表达式的值当返回值。
返回目录
%% 根据性别区分敬语
greet(male, Name) ->io:format("Hello, Mr. ~s", [Name]);
greet(female, Name) ->io:format("Hello, Mrs. ~s", [Name]);
greet(_, Name) ->io:format("Hello, ~s!", [Name]).
valid_time({ Date = { Y, M, D }, Time = { H, Min, S} }) ->io:format("The Date tuple (~p) says today is: ~p/~p/~p, ~n", [Date, Y, M, D]),io:format("The Time tuple (~p) indicates: ~p:~p:~p. ~n", [Time, H, Min, S]);
valid_time(_) ->io:format("Stop feeding me wrong data!~n").
返回目录
关卡是模式匹配的一种扩展。
可以对某个模式里的变量执行简单的测试和比较。
示例:求两个数的最大值
is_positive(X) when is_integer(X), X > 0 -> true;
is_positive(_) -> false.
当 X 为整型时,并且X 大于 0 时句 1 匹配,返回 true。
当句1不匹配时,句2匹配,返回 false。
使用在函数的头部使用关卡(由 when 引入);或在支持表达式的任何地方使用。
当关卡被用作表达式时,执行的结果是 true 或 false。
关卡由一系列关卡表达式组成,用逗号 “,” 分割。只有GuardExpr1, GuardExpr2, …,GuardExprN 等所有的关卡表达式都为 true 时才为 true
合法的关卡表达式是所有合法Erlang表达式的一个子集。关卡不能调用用户定义的函数,来确保关卡表达式的执行是无副作用的。
下列语法形式是合法的关卡表达式:
合法的关卡表达式 | 说明 |
---|---|
原子 true | |
其它常量(各种数据结构和已绑定变量),在关卡表达式里都为 false | |
调用后面表1里的关卡判断函数和表2里的内置函数 | |
数据结构比较 | 参考表6 |
算术表达式 | 参考表3 |
布尔表达式 | 参考8.7 |
短路布尔表达式 | 参考8.23 |
关卡序列是指单一或一系列的关卡,用分号“;”分隔。对于关卡序列G1; G2; …; Gn,只要其中一个关卡的值为 true,它的值就为 true。
关卡示例:
%% 判断两个值的大小
max(X, Y) when X > Y -> X; %% 当 X 大于 Y 时,返回 X
max(_, Y) -> Y. %% 如 X 不大于 Y,则返回 Y%% 当X是一个整数、并且 X 大于 Y,并且 Y 小于 6 时匹配
f(X,Y) when is_integer(X), X > Y, Y < 6 -> ...%% T是一个包含六个元素的元组,并且T中第三个元素的绝对值大于5
is_tuple(T), tuple_size(T) =:= 6, abs(element(3, T)) > 5%% 元组 X 的第 4 个元素与列表 L 的列表头相同
element(4, X) =:= hd(L)%% (X 等于 cat) 或者 (X 等于 dog)
X =:= dog; X =:= cat%% (X 是整数,并且 X 大于 Y) 或者 Y 的绝对值小于 23
is_integer(X), X > Y; abs(Y) < 23%% A 大于等于 -1 并且 A 加 1 小于 B
A >= -1.0 andalso A+1 > B%% 如果 L 是个原子,那么判断 L 是个列表 并且 L 的长度大于 2
is_atom(L) orelse ( is_list(L) andalso length(L) > 2)
表1 返回关卡
判断函数 | 说明 |
---|---|
is_atom(X) | X是一个原子 |
is_binary(X) | X是一个二进制型 |
is_constant(X) | X是一个常量 |
is_float(X) | X是一个浮点数 |
is_function(X) | X是一个fun |
is_function(X, N) | X是一个带有N个参数的fun |
is_integer(X) | X是一个整数 |
is_list(X) | X是一个列表 |
is_map(X) | X是一个映射组 |
is_number(X) | X是一个整数或浮点数 |
is_pid(X) | X是一个进程标识符 |
is_pmod(X) | X是一个参数化模块的实例 |
is_port(X) | X是一个端口 |
is_reference(X) | X是一个引用 |
is_tuple(X) | X是一个元组 |
is_record(X,Tag) | X是一个类型为Tag的记录 |
is_record(X,Tag,N) | X是一个类型为Tag、大小为N的记录 |
表2 返回关卡
内置函数 | 说明 |
---|---|
abs(X) | X的绝对值 |
byte_size(X) | X的字节数,X必须是一个位串或二进制型 |
element(N, X) | X里的元素N,注意:X必须是一个元组 |
float(X) | 将X 转换成一个浮点数,X必须是一个数字 |
hd(X) | 列表 X 的列表头 |
length(X) | 列表X的长度 |
node() | 当前的节点 |
node(X) | 创建X的节点,X可以是一个进程、标识符、引用或端口 round(X) |
self() | 当前进程的进程标识符 |
size(X) | X的大小,它可以是一个元组或二进制型 |
trunc(X) | 将X去掉小数部分取整,X必须是一个数字 |
tl(X) | 列表X的列表尾 |
tuple_size(T) | 元组T的大小 |
返回目录
if 表达式的语法:
if Guard1 -> Expr_seq1;Guard2 -> Expr_seq1;...GuardN -> Expr_seqN
end, %% 多个 if 用逗号分隔
if ...
end. %% 用英文句号结束
如果 Guard1 的值为 true,那么执行 Expr_seq1,if 的值 为 Expr_seq1 的值
如果 Guard1 的值为 false,那么执行 Guard2。以此类推直到某个关卡成功为止
if 表达式必须至少有一个关卡的执行结果为 true。否则就会发生异常错误。
为了避免异常错误,在 if 表达式的最后添加一个 true 关卡。如想让异常错误发生,就不添加。
if A > 0 -> do_this()true -> true %% 避免if表达式没有值而报错
end
返回目录
case … of 表达式的语法:
case Expression ofPattern1 [when Guard1] -> Expr_seq1;Pattern2 [when Guard2] -> Expr_seq2;...
end
Expression 表达式;Pattern 模式匹配;Guard 关卡;Expr_seq 要执行的表达式
首先 Expression 被执行,其值为 Value。
随后 Value 轮流与 Pattern1(可选关卡)、Pattern2等模式进行匹配,直到匹配成功
发现匹配,相应的表达式序列就会执行,而表达式的结果就是 case 表达式的值
如果所有模式都不匹配,就会发生异常错误( exception )
filter( P, [H | T] ) ->case P(H) oftrue -> [ H | filter(P, T) ];false -> filter(P, T)end
filter( P, [] ) -> [].
case 不是必须的。下面只用模式匹配来定义 filter
filter( P, [H | T] ) -> filter( P(H), H, P, T );
filter( P, [] ) -> [].filter1(true, H, P, T) -> [H | filter(P, T)];
filter1(false, H, P, T) -> filter(P, T)
这个定义比较丑。必须建立额外的函数 filter1,并向 filter/2 传递所有参数
返回目录
函数 | 说明 |
---|---|
Lambda 函数 | 匿名函数 |
高阶函数 | 操作其它函数的函数 |
%% 定义一个函数
1> Fn = fun(X) -> 2*X end.%% 定义一个高阶函数,
%% 函数 F 作为 参数 和 返回值
2> HFn = fun(F,X) -> F(X) end.%% 调用高阶函数,
3> HFn(Fn, 9).
18
用匿名函数当参数1> lists:map( fun(X) -> 2*X end , [1, 2, 3, 4]).
[2,4,6,8]
1> Fruit = [apple, pear, orange].
2> Test = fun(L) -> (fun(X) -> lists:member(X, L) end) end.
3> IsFruit = Test(Fruit). 4> IsFruit(pear).
true
5> IsFruit(dog).
false
就是把返回值替换成函数即可返回目录
定义 for 循环
%% 模块名称
-module(my_lib).%% 导出模块内的方法
-export([for/3]).%% 实现 for 循环
for(Max, Max, F) -> [F(Max)];
for(I, Max, F) -> [F(I) | for(I+1, Max, F)].
Erlang Shell 中调用 for 循环
%% 编译
1> c(my_lib).%% 调用
2> my_lib:for(1, 10, fun(I) -> I end).
[1,2,3,4,5,6,7,8,9,10]
3> my_lib:for(1, 10, fun(I) -> I*I end).
[1,4,9,16,25,36,49,64,81,100]
在模块中使用 for 循环
%% 模块名称
-module(main).%% 导出模块内的方法
-export([run/3]).%% 导入模块 my_lib 的 for 方法
-import(my_lib, [for/3]).%% 必须在模块中的函数内调用引入的方法。
run(I, Max, F) ->my_lib:for(I, Max, F).
重入解析代码(reentrant parsing code)
解析组合器(parser combinator)
惰性求值器(lazy evaluator)
返回目录
记录其实就是元组的另一种形式
使用记录,可以给元组里的各个元素关联一个名称
-record(Name, {% 以下两个键带有默认值key1 = Default1,key2 = Default2,...% key3 = undefinedkey3,...
}).
% records.hrl
-record(todo, {status = reminder, who = joe, text}).
% 在 shell 里读取 包含记录的 .hrl 文件
1> rr("records.hrl").
[todo]
% 创建记录,所有的键都是原子,而且必须与记录定义里所用的一致
2> #todo{}.
#todo{status = reminder, who = joe, text=undefined}
3> X1 = #todo{ status = urgent, text = "Fix errata in book"}.
#todo{status = urgent, who = joe, text=undefined}
% 创建的是原始记录的副本
4> X2 = #todo{ status = done }.
#todo{status = done, who = joe, text = "Fix errata in book"}
% 提取记录字段
5> #todo{who = W, test = Txt} = X2.
#todo{status = done, who = joe, text = "Fix errata in book"}
6> W.
joe
7> Txt.
"Fix errata in book"
% 只提取单个字段,用 . 语法
8>
"Fix errata in book"
clear_status(#todo{status=S, who=W} = R) ->% 在此函数内部, S 和 W 绑定了记录里的字段值% R是整个记录R#todo{status=finished}% ...
do_something(X) when is_record(X, todo) ->%% ...
9> X2
#todo{status=done,who=joe,text="Fix errata in book"}
10> rf(todo). % 忘掉记录todo的定义
11> X2.
{todo,done,joe,"Fix errata in book"}
返回目录
键-值对的关联性集合
键可以是任意的Erlang数据类型
比元组占用更多的存储空间,查找起来也更慢
映射组适合以下的情形:
语法
% 创建映射组
Map = #{ Key1 => Val1, Key2 => Val2, ... KeyN => ValN }.
% 更新映射组
NewMap = OldMap#{K1 := V1, ...,Kn := Vn}.
举例
% 创建映射组
1> F1 = #{ a => 1, b => 2 }.
#{a => 1,b => 2}
2> Facts = #{{wife, fred} => "Sue",{age, fred} => 45,{daughter, fred} => "Mary",{likes, jim} => ["Man"]
}.
% 替换映射组
3> F2 = F1#{ c => xx }. % 新建键值 c
#{a => 1,b => 2,c => xx}
4> F3 = F1#{ c := 3 }. % 错误,变量 F1 里没有 c 这个 key
5> F3 = F2#{ c := 6 }. % ok,F3 的 c 值为 6
% 取值
6> #{ c := X } = F3. % 单字段提取 X = 6
7> #{ d := Y } = F3. % 错误,匹配不到右边的值
8> #{ Z => 1547}. % 错误,变量 Z 未绑定
模式匹配映射组字段
可以在函数的头部使用包含模式的映射组,前提是映射组里所有的键都是已知的
% 定义一个 count_characters(Str) 函数
% 让它返回一个映射组,内含字符串里各个字符的出现次数
% 这个例子运行不起来 以后在考究
count_characters(Str) ->count_characters(Str, #{}).count_characters([H | T], #{ H => N } = X) ->count_characters(T, X#{ H := N+1 });
count_characters([H | T], X) ->count_characters(T, X#{ H => 1 });
count_characters([], X) ->X.
执行
1> count_characters("hello").
#{101=>1,104=>1,1-8=>2,111=>1}
方法
maps:new() -> #{} %返回一个空的映射组
erlang:is_map(M) %如果M是映射组返回true否则返回false。可以用在关卡测试或函数主体中。
map:to_list(M) -> [{K1, V1}, … ,{Kn, Vn}] %把映射组M里的所有键和值转换成一个键值列表,键值在生成的列表里严格按照升序排列。
maps:from_list([{K1, V1}, …, {Kn, Vn}]) -> M %把一个包含键值对的列表转换成一个映射组M。如果同样的键不止一次的出现,就使用列表里第一键所关联的值,后续的值都会被忽略。
maps:size(Map) -> numberOfEntries %返回映射组里的条目数量。
maps:is_key(Key, Map) -> boolean()%如果映射组包含一个键未key的项就返回true,否则返回false。
maps:get(Key, Map) -> val %返回映射组里与Key关联的值,否则抛出一个异常错误。
maps:find(Key, Map) -> {ok, Value} | error。%返回映射组与Key关联的值,否则返回error。
maps:keys(Map) -> [Key1, …, KeyN] %返回映射组所含的键列表,按升序排序
maps:remove(Key, M) -> M1%返回一个新映射组M1,除了键未Key的项(如果有的话)被移除外,其他与M一致。
maps:without([Key1, …, KeyN], M) -> M1 %返回一个新映射组M1,它是M的复制,但移除了带有[Key1,…, KeyN]列表里这些键的元素。
maps:difference(M1, M2) -> M3 %M3是M1的复制,但移除了那些与M2里的元素具有相同键的元素
%他的行为类似于下面的定义
maps:difference(M1, M2) ->
maps:without(maps:keys(M2), M1).
6. 映射组排序
映射组在比较时首先会比大小,然后再按照键的排序比较键和值。
如果A和B是映射组,那么当maps:size(A) < maps:size(B)时A < B。
如果A和B是大小相同的映射组,那么当maps:to_list(A) < maps:to_list(B)时A < B。
举个例子,A = #{age => 23, person => “jim”}小于B = # {email => “sue@somplace”, name => “sue”}。这是因为A的最小键(age)比B的最小键(email)更小。
当映射组与其他Erlang数据类型相比较时,因为我们认为映射组比列表或元组“更复杂”,所以映射组总是会大于列表或元组
映射组可以通过io:format里的~p选项输出,并用io:read或file:consult读取。
7. 以JSON为桥梁
有两个内置函数可以让映射组和JSON数据相互转换
内置函数 | 说明 |
---|---|
maps:to_json(Map) -> Bin | 把一个映射组转换成二进制型,它包含用JSON表示的该映射组请。注意,不是所有的映射组都能转换成JSON数据类型。映射组里所有的值都必须是能用JSON表示的对象。例如,值不能包含的对象有fun、进程标识符和引用等。如果有任何的键或值不能用JSON表示,maps:to_json就会失败。 |
maps:from_json(Bin) -> Map | 把一个包含JSON数据的二进制型转换成映射组。 |
maps:safe_from_json(Bin) -> Map | 把一个包含JSON数据的二进制型转换成映射组。Bin里的任何原子必须在调用此内置函数前就已存在,否则就会抛出一个异常错误。这样做是为了防止创建大量的新原子。出于效率的原因,Erlang不会垃圾回收(garbage collect)原子,所以连续不断地添加新原子会(在很长一段时间后)让Erlang虚拟机崩溃。 |
上面两种定义里的Map都必须是json_map()类型的实例
- type json_map() = [{json_key(), json_value()}].
- type json_key() = atom() | binary() | io_list() 并且
- type json_value() = integer() | binary() | float() | atom() | [json_value] | json_map()
JSON对象与Erlang值的映射关系如下。
- SON的数字用Erlang的整数或浮点数表示。
- JSON的字符串用Erlang的二进制型表示。
- JSON的列表用Erlang的列表表示。
- JSON的true和false用Erlang的原子true和false表示。
- JSON的对象用Erlang的映射组表示,但是有限制:映射组里的键必须是原子、字符串或二进制型,而值必须可以用JSON的数据类型表示。
当来回转换JSON数据类型时,应当注意一些特定的转换限制。Erlang对整数提供了无限的精度。所以,Erlang会很自然地把映射组里的某个大数转换成JSON数据里的大数,而解码此JSON数据的程序不一定能理解它。
返回目录
定义快速排序
-module(my_lib).
-export([qsort/1]).qsort( [] ) -> [];
qsort( [ Pivot | T ] ) ->qsort( [ X || X <- T, X < Pivot ] )++ [ Pivot ] ++qsort( [ X || X <- T, X>= Pivot ] ).
++ 操作符表示连接列表,代码优雅,但效率不行。
调用
1> c(my_lib).
2> L = [23,6,2,9,27,400,78,45,61,82,14]
3> my_lib:qsort(L).
[2,6,9,14,23,27,45,61,78,82,400]
1、参数 [Povot | T] 产生绑定
#伪代码
[ Pivot | T ]
= Pivot = 23, T = [6,2,9,27,400,78,45,61,82,14]
2、将列表 T 分成两个表,一个包含 T 里所有小于 Pivot 的元素,另一个包含所有大于等于 Pivot 的元素:
#伪代码
Smaller = [ X || X <- T, X < Pivot ].
= [6,2,9,14]
Bigger = [ X || X <- T, X >= Pivot ].
= [27,400,78,45,61,82]
3、递归排序 Smaller 和 Bigger,并将它们与 Pivot 合并:
#伪代码
qsort( [6,2,9,14] ) ++ [23] ++ qsort( [27,400,78,45,61,82] )
= [2,6,9,14] ++ [23] ++ [27,45,61,78,82,400]
= [2,6,9,14,23,27,45,61,78,82,400]
返回目录
pythag(N) ->[ {A,B,C} ||A <- lists:seq(1, N), %% A 提取列表[1,..N]里元素B <- lists:seq(1, N), %% B 提取列表[1,..N]里元素C <- lists:seq(1, N), %% C 提取列表[1,..N]里元素A+B+C =< N, %% 条件1:A 加 B 加 C 小于等于 NA*A+B*B =:= C*C %% 条件2:并且 A方 加 B方 等于 C方].
返回目录
perms( [] ) -> [ [] ];
perms( L ) -> [ [ H | T ] || H <- L, T <- perms( L -- [ H ] ) ],
1、从 L 里 提取 H,然后从 perms( L – [H] )里提取 T,最后返回 [H | T]
2、H <- L 相当于一个for循环,在每次循环中又执行一次 T <- perms( L – [ H ] ) ,相当于循环嵌套,又相当于递归
返回目录
返回目录
%% 模块名称
-module(my_lib).%% 导出模块内的方法
-export([sum/1]).sum( [H | T] ) -> H + sum(T); %% 递归调用
sum( [] ) -> 0. %% 为空,返回 0
两个 sum 的顺序不重要,因为两种情况是互斥的。%% 编译
1> c(my_lib).%% 调用
2> my_lib:sum([1,2,3,4,5]).
15
返回目录
%% 模块名称
-module(my_lib).%% 导出模块内的方法
-export([map/2]).%% 两个参数,一个处理函数,一个要处理的 List
map(_, []) -> []; %% 空 List 返回空 List
map(F, [H|T]) -> [F(H)|map(F,T)]. %% 递归调用
1> c(my_lib).
2> my_lib:map(fun(X) -> 2*X end, [1,2,3,4,5]).
[2,4,6,8,10]
返回目录
-module(shop).
-export([cost/1]).cost(oranges) -> 5;
cost(newspaper) -> 8;
cost(apple) -> 2;
cost(pears) -> 9;
cost(milk) -> 7;
-module(shop1).
-export([totle/1]).total([{What, N} | T]) -> shop:cost(What) * N + total(T);
total([]) -> 0.
$ erl
1> c(shop).
{ok,shop}
2> c(shop1).
{ok,shop1}
3> shop1:total([]).
0
4> al([{milk, 3}]).
21
语句匹配所得值为:What = milk,N = 3,T = []5> shop1:total([{pears, 6}, {milk, 3}]).
75
语句匹配所得值为:What = pears,N = 6,T = [{milk, 3}]6> Buy = [{oranges, 4}, {newspaper, 1}, {apple, 10}, {pears, 6}, {milk, 3}].
7> shop1:total(Buy).
123
返回目录
```erlang
% l
% 不推荐,这样会遍历2次列表
odds_and_evens1(L) ->Odds = [X || X <- L, (X rem 2) =:= 1],Evens = [X || X <- L, (X rem 2) =:= 0],{Odds, Evens}.% 推荐,只遍历列表1次,把奇偶数分别添加到适合的列表里。
odds_and_evens2(L) -> odds_and_evens_acc(L, [], []).
odds_and_evens_acc([H|T], Odds, Evens) ->case (H rem 2) fo1 -> odds_and_evens_acc(T, [H|Odds], Evens);0 -> odds_and_evens_acc(T, Odds, [H|Evens]);end;
odds_and_evens_acc([], Odds, Evens) ->{ lists:reverse(Odds), lists:reverse(Evens) }. % 得出得结果是反得,需要逆转
```
执行
```shell
1> lib_misc:odds_and_evens1([1,2,3,4,5,6]).
{[1,3,5], [2,4,6]}
2> lib_misc:odds_and_evens2([1,2,3,4,5,6]).
{[1,3,5], [2,4,6]}
```
返回目录
此内置函数能调用某个模块里的某个函数,并向它传递参数
与直接调用函数的区别在于模块名和/或函数名可以是动态计算得出的
尽量避免使用 apply
```apply(Mod, Func, [Arg1, Arg2, ..., ArgN])```动态调用
```Mod:Func(Arg1, Arg2, ..., ArgN)```效果相同,静态调用
1. 所有的Erlang内置函数也可以通过apply进行调用,```shell1> apply(erlang, atom_to_list, [hello]).```
2. Mod 参数不必是一个原子,也可以是一个元组
```{Mod, P1, P2, ..., Pn}:Func(A1,A2,...An)```等同于
```Mod:Func(A1,A2,...,An,{Mod,P1,P2,...,Pn})```
返回目录
一个函数的元数(arity)是该函数所拥有的参数数量。
同一模块里的两个名称相同、元数不同的函数是完全不同的函数。
Erlang程序员经常将名称相同、元数不同的函数作为辅助函数使用。
```erlang
% 元数为 1
sum(L) -> sum(L, 0). % 累加列表 L 里的所有元素
% 元数为 2 辅助函数,不导出来隐藏它们
sum([], N) -> N;
sum([H|T], N) -> sum(T, H+N).
```
本文发布于:2024-02-02 10:04:25,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170683946643072.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |