数组
简介
在 OCaml 中,数组是一组相同类型元素的集合。与列表不同,数组可以通过用相同类型其他值替换元素来进行修改,但不能调整大小。数组还允许高效地访问任何位置的元素。
尽管存在这些差异,但许多可用于数组的函数与可用于列表的函数类似。有关这些函数的更多详细信息,请参考 列表教程 和 文档。
本教程旨在介绍 OCaml 中数组的概念,并展示最有用的函数和用例。
数组通常在 OCaml 中用于以下任务:
- 存储和处理大量数据
- 实现需要随机访问和修改元素的算法
- 处理矩阵和其他多维数据结构
创建数组
要在 OCaml 中创建数组,可以使用 [| ...; ... |]
语法,该语法允许您直接指定每个元素的值。例如,要创建包含值 1、2、3、4 和 5 的数组,您将编写 [| 1; 2; 3; 4; 5 |]
# [| 1; 2; 3; 4; 5 |];;
- : int array = [|1; 2; 3; 4; 5|]
或者,您可以使用 Array.make
函数创建数组,该函数接受两个参数:数组的长度和每个元素的初始值。例如,要创建长度为 5 且所有元素都初始化为 0 的数组,您可以编写
# let zeroes = Array.make 5 0;;
val zeroes : int array = [|0; 0; 0; 0; 0|]
Array.init
通过将函数应用于数组的每个索引(从 0 开始)来生成给定长度的数组。以下代码行使用将参数加倍的函数创建包含前 5 个偶数的数组
# let even_numbers = Array.init 5 (fun i -> i * 2);;
val even_numbers : int array = [|0; 2; 4; 6; 8|]
访问数组元素
您可以使用 .(index)
语法访问数组的单个元素,其中 index
是要访问的元素的索引。第一个元素的索引为 0,最后一个元素的索引为数组大小减 1。例如,要访问数组 even_numbers
的第三个元素,您将编写
# even_numbers.(2);;
- : int = 4
修改数组元素
要修改数组中的元素,我们只需使用索引运算符为其分配一个新值。例如,要将上面创建的数组 even_numbers
的第三个元素的值更改为 42,我们必须编写
# even_numbers.(2) <- 42;;
- : unit = ()
请注意,此操作返回 unit
,而不是修改后的数组。even_numbers
会作为副作用就地修改。
Array
模块
标准库 OCaml 提供了许多用于处理数组的有用函数。以下是一些最常见的函数:
数组的长度
Array.length
函数返回数组的大小
# Array.length even_numbers;;
- : int = 5
遍历数组
Array.iter
将函数一次应用于数组的每个元素。给定函数必须返回 unit
,通过副作用进行操作。要打印上面创建的数组 zeroes
的所有元素,我们可以将 print_int
应用于每个元素
# Array.iter (fun x -> print_int x; print_string " ") zeroes;;
0 0 0 0 0 - : unit = ()
也可以使用 for
循环遍历数组。以下是用循环实现的相同示例
# for i = 0 to Array.length zeroes - 1 do
print_int zeroes.(i);
print_string " "
done;;
0 0 0 0 0 - : unit = ()
映射数组
Array.map
函数通过将给定函数应用于数组的每个元素来创建一个新数组。例如,我们可以获得一个包含 even_numbers
数组中每个数字平方的数组
# Array.map (fun x -> x * x) even_numbers;;
- : int array = [|0; 4; 1764; 36; 64|]
折叠数组
要将数组的所有元素组合成一个结果,我们可以使用 Array.fold_left
和 Array.fold_right
函数。这些函数接受一个二元函数、一个初始累加器值和一个数组作为参数。二元函数接受两个参数:累加器的当前值和数组的当前元素,然后返回一个新的累加器值。这两个函数都遍历数组,但方向相反。这与 List.fold_left
和 List.fold_right
本质上相同。
以下是 Array.fold_left
的签名
# Array.fold_left;;
val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b array -> 'a = <fun>
fold_left f init a
计算 f (... (f(f init a.(0)) a.(1)) ...) a.(n-1)
类似地,我们可以使用 Array.fold_right
函数,它交换了参数的顺序
# Array.fold_right;;
val fold_right : ('b -> 'a -> 'a) -> 'b array -> 'a -> 'a = <fun>
fold_right f a init
计算 f a.(0) (f a.(1) ( ... (f a.(n-1) init) ...))
这些函数从整个数组中推导出一个值。例如,它们可用于查找数组的最大元素
# Array.fold_left Int.max min_int even_numbers;;
- : int = 42
排序数组
要排序数组,我们可以使用 Array.sort
函数。此函数接受以下参数:
- 一个比较函数
- 一个数组 它根据提供的比较函数对提供的数组进行就地排序,并按升序排序。
Array.sort
执行的排序会修改提供的数组的内容,这就是它返回unit
的原因。例如,要对上面创建的数组even_numbers
进行排序,我们可以使用
# Array.sort compare even_numbers;;
- : unit = ()
# even_numbers;;
- : int array = [|0; 2; 6; 8; 42|]
将数组的一部分复制到另一个数组中
Array.blit
函数可以高效地将数组的连续部分复制到另一个数组中。类似于 array.(x) <- y
操作,该函数会就地修改目标数组并返回 unit
,而不是修改后的数组。假设您想将 ones
的一部分复制到 zeroes
中
# let ones = Array.make 5 1;;
val ones : int array = [|1; 1; 1; 1; 1|]
# Array.blit ones 0 zeroes 1 2;;
- : unit = ()
# zeroes;;
- : int array = [|0; 1; 1; 0; 0|]
这将从索引 0
开始复制 ones
的两个元素(此数组切片为 [| 1; 1 |]
)到 zeroes
中,从索引 1
开始。您需要确保提供的两个索引在各自的数组中有效,并且要复制的元素数量在每个数组的范围内。
我们也可以使用此函数将数组的一部分复制到自身
# Array.blit zeroes 1 zeroes 3 2;;
- : unit = ()
# zeroes;;
- : int array = [|0; 1; 1; 1; 1|]
这将从索引 1
开始复制 zeroes
的两个元素到 zeroes
的最后部分,从索引 3
开始。
结论
在本教程中,我们介绍了 OCaml 中数组的基础知识,包括如何创建和操作它们,以及一些最实用的函数和用例。请参考 标准库文档 以浏览 Array
模块的所有函数列表。