3 数据类型
Erlang 提供了许多数据类型,在本节中列出。
请注意,Erlang 没有用户定义的类型,只有由 Erlang 项组成的复合类型(数据结构)。这意味着任何测试复合类型的函数,通常命名为 is_type/1,可能会对与所选表示形式一致的项返回 true。内置类型的对应函数不会出现这种情况。
3.1 项
任何数据类型的任何数据块都称为 **项**。
3.2 数字
有两种类型的数字文字,**整数** 和 **浮点数**。除了传统表示法之外,还有两种 Erlang 特定的表示法
-
$char
字符 **char** 的 ASCII 值或 Unicode 代码点。 -
base#value
以 **base** 为基的整数,必须是 2..36 范围内的整数。
前导零将被忽略。单个下划线 _ 可以插入数字之间作为视觉分隔符。
示例
1> 42. 42 2> -1_234_567_890. -1234567890 3> $A. 65 4> $\n. 10 5> 2#101. 5 6> 16#1f. 31 7> 16#4865_316F_774F_6C64. 5216630098191412324 8> 2.3. 2.3 9> 2.3e3. 2.3e3 10> 2.3e-3. 0.0023 11> 1_234.333_333 1234.333333
浮点数的表示
在使用浮点数时,您可能无法在打印或执行算术运算时看到您期望的结果。这是因为浮点数在以 2 为基的系统中用固定数量的位表示,而打印的浮点数则在以 10 为基的系统中表示。Erlang 使用 64 位浮点数。以下是这种现象的示例
> 0.1+0.2.
0.30000000000000004
实数 0.1 和 0.2 无法精确地表示为浮点数。
> {36028797018963968.0, 36028797018963968 == 36028797018963968.0,
36028797018963970.0, 36028797018963970 == 36028797018963970.0}.
{3.602879701896397e16, true,
3.602879701896397e16, false}.
值 36028797018963968 可以精确地表示为浮点值,但 Erlang 的漂亮打印机将 36028797018963968.0 舍入为 3.602879701896397e16 (=36028797018963970.0),因为范围 [36028797018963966.0, 36028797018963972.0] 中的所有值都由 36028797018963968.0 表示。
有关浮点数及其问题的更多信息,请参见
如果您需要使用小数,例如需要表示货币,那么您应该使用处理小数的库,或者以美分而不是欧元进行操作,这样您就不需要小数。
另外请注意,Erlang 的浮点数与 IEEE 754 浮点数并不完全匹配,因为 Erlang 不支持 Inf 或 NaN。任何会导致 NaN、+Inf 或 -Inf 的操作将改为引发 badarith 异常。
3.3 原子
原子是文字,是带名称的常量。如果原子不以小写字母开头,或者包含除字母数字字符、下划线 (_) 或 @ 之外的其他字符,则应将其用单引号 (') 括起来。
示例
hello phone_number 'Monday' 'phone number'
3.4 比特串和二进制
比特串用于存储未类型化的内存区域。
比特串使用 比特语法 表示。
包含可被 8 整除的位数的比特串称为 **二进制**
示例
1> <<10,20>>. <<10,20>> 2> <<"ABC">>. <<"ABC">> 1> <<1:1,0:1>>. <<2:2>>
有关更多示例,请参见 编程示例。
3.5 引用
连接节点之间唯一的项。引用可以通过调用 make_ref/0 BIF 来创建。可以使用 is_reference/1 BIF 来测试项是否为引用。
3.6 函数
函数是函数对象。函数使创建匿名函数并传递函数本身(而不是其名称)作为其他函数的参数成为可能。
示例
1> Fun1 = fun (X) -> X+1 end. #Fun<erl_eval.6.39074546> 2> Fun1(2). 3
在 函数表达式 中阅读有关函数的更多信息。有关更多示例,请参见 编程示例。
3.7 端口标识符
端口标识符标识 Erlang 端口。
open_port/2 用于创建端口,它返回这种数据类型的值。
在 端口和端口驱动 中阅读有关端口的更多信息。
3.8 PID
PID 是进程标识符的缩写。每个进程都有一个 PID,用于标识该进程。PID 在连接节点上的活动进程中是唯一的。但是,终止进程的 PID 在一段时间后可以被用作新进程的 PID。
BIF self/0 返回调用进程的 PID。当 创建新进程 时,父进程可以通过返回值(例如调用 spawn/3 BIF 时)或通过消息(例如调用 spawn_request/5 BIF 时)获取子进程的 PID。PID 通常用于向进程发送 信号。可以使用 is_pid/1 BIF 来测试项是否为 PID。
示例
-module(m). -export([loop/0]). loop() -> receive who_are_you -> io:format("I am ~p~n", [self()]), loop() end. 1> P = spawn(m, loop, []). <0.58.0> 2> P ! who_are_you. I am <0.58.0> who_are_you
在 进程 中阅读有关进程的更多信息。
3.9 元组
元组是一种包含固定数量项的复合数据类型
{Term1,...,TermN}
元组中的每个项 Term 都称为 **元素**。元素的数量称为元组的 **大小**。
存在许多 BIF 来操作元组。
示例
1> P = {adam,24,{july,29}}. {adam,24,{july,29}} 2> element(1,P). adam 3> element(3,P). {july,29} 4> P2 = setelement(2,P,25). {adam,25,{july,29}} 5> tuple_size(P). 3 6> tuple_size({}). 0
3.10 映射
映射是一种包含可变数量的键值关联的复合数据类型
#{Key1=>Value1,...,KeyN=>ValueN}
映射中的每个键值关联都称为 **关联对**。对的键和值部分称为 **元素**。关联对的数量称为映射的 **大小**。
存在许多 BIF 来操作映射。
示例
1> M1 = #{name=>adam,age=>24,date=>{july,29}}. #{age => 24,date => {july,29},name => adam} 2> maps:get(name,M1). adam 3> maps:get(date,M1). {july,29} 4> M2 = maps:update(age,25,M1). #{age => 25,date => {july,29},name => adam} 5> map_size(M). 3 6> map_size(#{}). 0
在 STDLIB 的 maps 手册页中可以找到映射处理函数的集合。
在 映射表达式 中阅读有关映射的更多信息。
映射在 Erlang/OTP R17 中作为一项实验性功能引入。它们的功能在 Erlang/OTP 18 中得到了扩展并得到完全支持。
3.11 列表
列表是一种包含可变数量项的复合数据类型。
[Term1,...,TermN]
列表中的每个项 Term 都称为 **元素**。元素的数量称为列表的 **长度**。
正式地,列表要么是空列表 [],要么包含一个 **头部**(第一个元素)和一个 **尾部**(列表的剩余部分)。**尾部** 也是一个列表。后者可以表示为 [H|T]。上面的表示法 [Term1,...,TermN] 等效于列表 [Term1|[...|[TermN|[]]]]。
示例
[] 是一个列表,因此
[c|[]] 是一个列表,因此
[b|[c|[]]] 是一个列表,因此
[a|[b|[c|[]]]] 是一个列表,或简写为 [a,b,c]
尾部为列表的列表有时称为 **正确列表**。允许列表的尾部不是列表,例如 [a|b]。但是,这种类型的列表在实际应用中很少使用。
示例
1> L1 = [a,2,{c,4}]. [a,2,{c,4}] 2> [H|T] = L1. [a,2,{c,4}] 3> H. a 4> T. [2,{c,4}] 5> L2 = [d|T]. [d,2,{c,4}] 6> length(L1). 3 7> length([]). 0
在 STDLIB 的 lists 手册页中可以找到列表处理函数的集合。
3.12 字符串
字符串用双引号 (") 括起来,但在 Erlang 中不是数据类型。相反,字符串 "hello" 是列表 [$h,$e,$l,$l,$o] 的简写,即 [104,101,108,108,111]。
两个相邻的字符串文字将连接成一个。这是在编译时完成的,因此不会产生任何运行时开销。
示例
"string" "42"
等效于
"string42"
3.13 记录
记录是一种用于存储固定数量元素的数据结构。它具有命名的字段,类似于 C 语言中的结构体。但是,记录不是真正的數據類型。相反,记录表达式在编译期间会被转换为元组表达式。因此,除非采取特殊措施,否则 shell 无法理解记录表达式。有关详细信息,请参阅 STDLIB 中的 shell(3) 手册页。
示例
-module(person).
-export([new/2]).
-record(person, {name, age}).
new(Name, Age) ->
#person{name=Name, age=Age}.
1> person:new(ernie, 44).
{person,ernie,44}
在 Records 中了解更多关于记录的信息。更多示例可以在 Programming Examples 中找到。
3.14 布尔值
Erlang 中没有布尔值数据类型。而是使用原子 true 和 false 来表示布尔值。
示例
1> 2 =< 3. true 2> true or false. true
3.15 转义序列
在字符串和带引号的原子中,识别以下转义序列
序列 | 描述 |
\b | 退格键 (ASCII 码 8) |
\d | 删除键 (ASCII 码 127) |
\e | 转义键 (ASCII 码 27) |
\f | 换页 (ASCII 码 12) |
\n | 换行符 (ASCII 码 10) |
\r | 回车键 (ASCII 码 13) |
\s | 空格 (ASCII 码 32) |
\t | (水平) 制表符 (ASCII 码 9) |
\v | 垂直制表符 (ASCII 码 11) |
\XYZ, \YZ, \Z | 八进制表示为 XYZ、YZ 或 Z 的字符 |
\xXY | 十六进制表示为 XY 的字符 |
\x{X...} | 十六进制表示的字符;X... 是一个或多个十六进制字符 |
\^a...\^z \^A...\^Z |
控制键 A 到控制键 Z |
\^@ | 空字符 (ASCII 码 0) |
\^[ | 转义键 (ASCII 码 27) |
\^\ | 文件分隔符 (ASCII 码 28) |
\^] | 组分隔符 (ASCII 码 29) |
\^^ | 记录分隔符 (ASCII 码 30) |
\^_ | 单元分隔符 (ASCII 码 31) |
\^? | 删除键 (ASCII 码 127) |
\' | 单引号 |
\" | 双引号 |
\\ | 反斜杠 |
从 Erlang/OTP 26 开始,$\^? 的值已更改为 127 (删除键),而不是 31。之前的版本允许 $\^ 后面的任何字符;从 Erlang/OTP 26 开始,只允许使用已记录的字符。
3.16 类型转换
有许多用于类型转换的 BIF。
示例
1> atom_to_list(hello). "hello" 2> list_to_atom("hello"). hello 3> binary_to_list(<<"hello">>). "hello" 4> binary_to_list(<<104,101,108,108,111>>). "hello" 5> list_to_binary("hello"). <<104,101,108,108,111>> 6> float_to_list(7.0). "7.00000000000000000000e+00" 7> list_to_float("7.000e+00"). 7.0 8> integer_to_list(77). "77" 9> list_to_integer("77"). 77 10> tuple_to_list({a,b,c}). [a,b,c] 11> list_to_tuple([a,b,c]). {a,b,c} 12> term_to_binary({a,b,c}). <<131,104,3,100,0,1,97,100,0,1,98,100,0,1,99>> 13> binary_to_term(<<131,104,3,100,0,1,97,100,0,1,98,100,0,1,99>>). {a,b,c} 14> binary_to_integer(<<"77">>). 77 15> integer_to_binary(77). <<"77">> 16> float_to_binary(7.0). <<"7.00000000000000000000e+00">> 17> binary_to_float(<<"7.000e+00">>). 7.0