查看源代码 基本类型

在本节中,我们将深入了解 Elixir 的基本类型:整数、浮点数、布尔值、原子和字符串。其他数据类型,如列表和元组,将在下一节中探讨。

iex> 1          # integer
iex> 0x1F       # integer
iex> 1.0        # float
iex> true       # boolean
iex> :atom      # atom / symbol
iex> "elixir"   # string
iex> [1, 2, 3]  # list
iex> {1, 2, 3}  # tuple

基本算术运算

打开 iex 并输入以下表达式

iex> 1 + 2
3
iex> 5 * 5
25
iex> 10 / 2
5.0

请注意,10 / 2 返回的是浮点数 5.0,而不是整数 5。这是预期的结果。在 Elixir 中,运算符 / 始终返回浮点数。如果要进行整数除法或获取除法的余数,可以使用 divrem 函数

iex> div(10, 2)
5
iex> div 10, 2
5
iex> rem 10, 3
1

请注意,Elixir 允许在调用期望一个或多个参数的函数时省略括号。此功能在编写声明和控制流结构时提供更简洁的语法。但是,Elixir 开发人员通常更喜欢使用括号。

Elixir 还支持输入二进制、八进制和十六进制数字的简写形式

iex> 0b1010
10
iex> 0o777
511
iex> 0x1F
31

浮点数需要一个点后跟至少一位数字,并且也支持 e 来表示科学计数法

iex> 1.0
1.0
iex> 1.0e-10
1.0e-10

Elixir 中的浮点数精度为 64 位。

可以使用 round 函数获取最接近给定浮点数的整数,或使用 trunc 函数获取浮点数的整数部分。

iex> round(3.58)
4
iex> trunc(3.58)
3

最后,当我们使用不同的数据类型时,我们将了解 Elixir 提供了几个谓词函数来检查值的类型。例如,可以使用 is_integer 检查值是否为整数

iex> is_integer(1)
true
iex> is_integer(2.0)
false

还可以使用 is_floatis_number 分别检查参数是否为浮点数,或同时为整数或浮点数。

识别函数和文档

在我们继续下一个数据类型之前,让我们讨论一下 Elixir 如何识别函数。

Elixir 中的函数由它们的名称和元数标识。函数的元数描述了函数接受的参数数量。从现在开始,我们将使用函数名称及其元数来描述整个文档中的函数。 trunc/1 标识名为 trunc 且接受 1 个参数的函数,而 trunc/2 标识具有相同名称但元数为 2 的另一个(不存在)函数。

我们也可以使用这种语法访问文档。Elixir shell 定义了 h 函数,可以使用它访问任何函数的文档。例如,输入 h trunc/1 将打印 trunc/1 函数的文档

iex> h trunc/1
                             def trunc()

Returns the integer part of number.

h trunc/1 工作原理是,它是在 Kernel 模块中定义的。Kernel 模块中的所有函数都自动导入到我们的命名空间中。大多数情况下,在查找给定函数的文档时,还会包含模块名称

iex> h Kernel.trunc/1
                             def trunc()

Returns the integer part of number.

可以使用模块+函数来查找任何内容,包括运算符(尝试 h Kernel.+/2)。调用不带参数的 h 将显示 IEx.Helpers 的文档,其中定义了 h 和其他功能。

布尔值和 nil

Elixir 支持 truefalse 作为布尔值

iex> true
true
iex> true == false
false

Elixir 还提供了三个布尔运算符:or/2and/2not/1。这些运算符是严格的,因为它们期望第一个参数是评估为布尔值(truefalse)的值

iex> true and true
true
iex> false or is_boolean(true)
true

提供非布尔值将引发异常

iex> 1 and true
** (BadBooleanError) expected a boolean on left-side of "and", got: 1

orand 是短路运算符。它们只在左侧不足以确定结果时才执行右侧

iex> false and raise("This error will never be raised")
false
iex> true or raise("This error will never be raised")
true

Elixir 还提供了 nil 的概念,用于指示值不存在,以及一组也操作 nil 的逻辑运算符:||/2&&/2!/1。对于这些运算符,falsenil 被认为是“假值”,所有其他值都被认为是“真值”。

# or
iex> 1 || true
1
iex> false || 11
11

# and
iex> nil && 13
nil
iex> true && 17
17

# not
iex> !true
false
iex> !1
false
iex> !nil
true

原子

原子是一个常量,其值与其名称相同。一些其他语言称其为符号。它们通常用于枚举不同的值,例如

iex> :apple
:apple
iex> :orange
:orange
iex> :watermelon
:watermelon

如果原子的名称相同,则它们相等。

iex> :apple == :apple
true
iex> :apple == :orange
false

它们通常用于表示操作的状态,使用 :ok:error 等值。

布尔值 truefalse 也是原子

iex> true == :true
true
iex> is_atom(false)
true
iex> is_boolean(:false)
true

Elixir 允许省略原子 falsetruenil 的前导 :

字符串

Elixir 中的字符串用双引号分隔,并且以 UTF-8 编码

iex> "hellö"
"hellö"

注意:如果您在 Windows 上运行,则终端可能默认情况下不使用 UTF-8。您可以在进入 IEx 之前运行 chcp 65001 来更改当前会话的编码。

可以使用 <>/2 运算符连接两个字符串

iex> "hello " <> "world!"
"hello world!"

Elixir 还支持字符串插值

iex> string = "world"
iex> "hello #{string}!"
"hello world"

字符串连接需要两侧都为字符串,但插值支持可以转换为字符串的任何数据类型

iex> number = 42
iex> "i am #{number} years old!"
"i am 42 years old!"

字符串中可以包含换行符。可以使用转义序列引入它们

iex> "hello
...> world"
"hello\nworld"
iex> "hello\nworld"
"hello\nworld"

可以使用 IO.puts/1 函数(来自 IO 模块)打印字符串

iex> IO.puts("hello\nworld")
hello
world
:ok

请注意,IO.puts/1 函数在打印后返回原子 :ok

Elixir 中的字符串在内部由称为二进制的连续字节序列表示

iex> is_binary("hellö")
true

我们还可以获取字符串中的字节数

iex> byte_size("hellö")
6

请注意,该字符串中的字节数为 6,即使它有 5 个字符。这是因为字符 "ö" 在 UTF-8 中需要 2 个字节来表示。可以使用 String.length/1 函数获取基于字符数量的字符串的实际长度

iex> String.length("hellö")
5

String 模块包含许多在 Unicode 标准中定义的字符串操作函数

iex> String.upcase("hellö")
"HELLÖ"

结构比较

Elixir 还提供了 ==!=<=>=<> 作为比较运算符。我们可以比较数字

iex> 1 == 1
true
iex> 1 != 2
true
iex> 1 < 2
true

还可以比较原子、字符串、布尔值等

iex> "foo" == "foo"
true
iex> "foo" == "bar"
false

如果整数和浮点数的值相同,则它们进行相同的比较

iex> 1 == 1.0
true
iex> 1 == 2.0
false

但是,如果您想区分整数和浮点数,可以使用严格比较运算符 ===!==(这是这两个运算符之间唯一的区别)

iex> 1 === 1.0
false

Elixir 中的比较运算符可以跨任何数据类型进行比较。我们说这些运算符执行 *结构比较*。有关更多信息,请阅读我们有关 结构比较与语义比较 的文档。

Elixir 还提供了用于表达集合的数据类型,例如列表和元组,我们将在下一节中学习。当我们讨论通过进程实现并发和容错时,我们还将讨论端口、pid 和引用,但这将在后面的章节中介绍。让我们继续前进。