Elixir学习笔记——列表和元组

在本章中,我们将学习 Elixir 中最常用的两种集合数据类型:列表和元组。

(链接)列表

Elixir 使用方括号来指定值列表。值可以是任何类型:

iex>[1, 2, true, 3]

[1, 2, true, 3]

iex>length([1, 2, 3])

3

可以分别使用 ++/2 和 --/2 运算符连接或减去两个列表:

iex>[1, 2, 3] ++ [4, 5, 6]

[1, 2, 3, 4, 5, 6]

iex>[1, true, 2, false, 3, true] -- [true, false]

[1, 2, 3, true]

列表运算符永远不会修改现有列表。连接或从列表中删除元素会返回一个新列表。我们说 Elixir 数据结构是不可变的。不变性的一个优点是它可以使代码更清晰。您可以自由地传递数据,并保证没有人会在内存中改变它 - 只会对其进行转换。

在整个教程中,我们将大量讨论列表的头部和尾部。头部是列表的第一个元素,尾部是列表的其余部分。可以使用函数 hd/1 和 tl/1 检索它们。让我们将列表分配给变量并检索其头部和尾部:

iex>list = [1, 2, 3]

iex>hd(list)

1

iex>tl(list)

[2, 3]

获取空列表的头部或尾部会引发错误:

iex>hd([])

** (ArgumentError) 参数错误

有时您会创建一个列表,它会返回一个以 ~c 开头的带引号的值。例如:

iex>[11, 12, 13]

~c"\v\f\r"

iex>[104, 101, 108, 108, 111]

~c"hello"

当 Elixir 看到可打印的 ASCII 数字列表时,Elixir 会将其打印为字符列表(实际上是字符列表)。字符列表在与现有 Erlang 代码交互时非常常见。每当您在 IEx 中看到一个值并且不太确定它是什么时,您可以使用 i/1 来检索有关它的信息:

iex>i ~c"hello"

Term

i ~c"hello"

Data type

List

Description

...

Raw representation

[104, 101, 108, 108, 111]

Reference modules

List

Implemented protocols

...

我们将在"二进制文件、字符串和字符列表"一章中更多地讨论字符列表。

单引号字符串
在 Elixir 中,您还可以使用"hello"来构建字符列表,但此表示法在 Elixir v1.15 中已被软弃用,并将在未来版本中发出警告。最好改写为 ~c"hello"。

元组

Elixir 使用花括号来定义元组。与列表一样,元组可以保存任何值:

iex>{:ok, "hello"}
{:ok, "hello"}

iex>tuple_size({:ok, "hello"})
2

元组将元素连续存储在内存中。这意味着通过索引访问元组元素或获取元组大小是一项快速操作。索引从零开始:

iex>tuple = {:ok, "hello"}

{:ok, "hello"}

iex>put_elem(tuple, 1, "world")

{:ok, "world"}

iex>tuple

{:ok, "hello"}

也可以使用 put_elem/3 将元素放在元组中的特定索引处:

tuple = {:ok, "hello"}
{:ok, "hello"}
put_elem(tuple, 1, "world")
{:ok, "world"}
tuple
{:ok, "hello"}

请注意,put_elem/3 返回了一个新元组。存储在 tuple 变量中的原始元组未被修改。与列表一样,元组也是不可变的。对元组的每个操作都会返回一个新元组,它永远不会更改给定的元组。

列表还是元组?

列表和元组有什么区别?
列表以链接列表的形式存储在内存中,这意味着列表中的每个元素都保存其值并指向下一个元素,直到到达列表末尾。这意味着访问列表的长度是一个线性操作:我们需要遍历整个列表才能确定其大小。
类似地,列表连接的性能取决于左侧列表的长度:
iex>list = [1, 2, 3]

[1, 2, 3]

这很快,因为我们只需要遍历 `[0]` 即可将其添加到 `列表` 的前面

iex>[0] ++ list
[0, 1, 2, 3]

这很慢,因为我们需要遍历 `列表` 以附加 4

iex>list++ [4]
[1, 2, 3, 4]
另一方面,元组在内存中连续存储。这意味着获取元组大小或通过索引访问元素的速度很快。另一方面,更新或向元组添加元素的成本很高,因为它需要在内存中创建新的元组:

iex>tuple = {:a, :b, :c, :d}
{:a, :b, :c, :d}
iex>put_elem(tuple, 2, :e)
{:a, :b, :e, :d}

但请注意,元素本身不会被复制。更新元组时,除已替换的条目外,所有条目都在旧元组和新元组之间共享。此规则适用于 Elixir 中的大多数数据结构。这减少了语言需要执行的内存分配量,并且只有由于语言的不可变语义才有可能。

这些性能特征决定了这些数据结构的使用。简而言之,当返回的元素数量可能有所不同时,使用列表。元组具有固定大小。让我们看两个来自 String 模块的例子:

iex>String.split("hello world")
["hello", "world"]
iex>String.split("hello beautiful world")
["hello", "beautiful", "world"]

String.split/2 函数将字符串拆分为每个空格字符上的字符串列表。由于返回的元素数量取决于输入,因此我们使用列表。

另一方面,String.split_at/2 在给定位置将字符串拆分为两部分。由于它总是返回两个条目,而不管输入大小如何,因此它返回元组:

iex>String.split_at("hello world", 3)
{"hel", "lo world"}
iex>String.split_at("hello world", -4)
{"hello w", "orld"}

使用元组和原子创建"标记元组"也很常见,当操作可能成功或失败时,这是一个方便的返回值。例如,File.read/1 读取给定路径下文件的内容,该路径可能存在也可能不存在。它返回带标签的元组:

iex>File.read("path/to/existing/file")
{:ok, "... content ..."}
iex>File.read("path/to/unknown/file")
{:error, :enoent}

如果 File.read/1 给出的路径存在,它将返回一个元组,其中原子 :ok 作为第一个元素,文件内容作为第二个元素。否则,它将返回一个元组,其中包含 :error 和错误描述。我们很快就会了解到,Elixir 允许我们在带标签的元组上进行模式匹配,并轻松处理成功和失败的情况。

鉴于 Elixir 始终遵循这些规则,随着您学习和使用该语言,列表和元组之间的选择会变得更加清晰。Elixir 通常会指导您做正确的事情。例如,有一个 elem/2 函数用于访问元组项:

iex>tuple = {:ok, "hello"}
{:ok, "hello"}
iex>elem(tuple, 1)
"hello"

但是,由于您通常不知道列表中元素的数量,因此除了列表的头部之外,没有内置等效函数用于访问列表中的任意条目。

大小还是长度?

在计算数据结构中的元素时,Elixir 还遵循一个简单的规则:如果操作是恒定时间(值是预先计算的),则函数名为 size;如果操作是线性的(计算长度的速度随着输入的增长而变慢),则函数名为 length。为了便于记忆,"length"和"linear"都以"l"开头。

例如,到目前为止,我们已经使用了 4 个计数函数:byte_size/1(用于字符串中的字节数)、tuple_size/1(用于元组大小)、length/1(用于列表长度)和 String.length/1(用于字符串中的字素数)。我们使用 byte_size 来获取字符串中的字节数,这是一种廉价的操作。另一方面,检索 Unicode 字素的数量使用 String.length/1,并且可能很昂贵,因为它依赖于对整个字符串的遍历。

现在我们已经熟悉了语言中的基本数据类型,在讨论更复杂的数据结构之前,让我们先学习编写代码的重要结构。

相关推荐
这孩子叫逆19 分钟前
Spring Boot项目的创建与使用
java·spring boot·后端
yunhuibin26 分钟前
ffmpeg面向对象——拉流协议匹配机制探索
学习·ffmpeg
Ljubim.te28 分钟前
软件设计师——数据结构
数据结构·笔记
hengzhepa36 分钟前
ElasticSearch备考 -- Search across cluster
学习·elasticsearch·搜索引擎·全文检索·es
蜡笔小新星1 小时前
Python Kivy库学习路线
开发语言·网络·经验分享·python·学习
coderWangbuer2 小时前
基于springboot的高校招生系统(含源码+sql+视频导入教程+文档+PPT)
spring boot·后端·sql
攸攸太上2 小时前
JMeter学习
java·后端·学习·jmeter·微服务
Kenny.志2 小时前
2、Spring Boot 3.x 集成 Feign
java·spring boot·后端
speop2 小时前
【笔记】I/O总结王道强化视频笔记
笔记·音视频
sky丶Mamba2 小时前
Spring Boot中获取application.yml中属性的几种方式
java·spring boot·后端