模块 Float.Array

module Array: sig .. end

具有打包表示的浮点数数组。


type t = floatarray 

具有打包表示的浮点数数组的类型。

val length : t -> int

返回给定浮点数数组的长度(元素数量)。

val get : t -> int -> float

get a n 返回浮点数数组 a 的第 n 个元素。

val set : t -> int -> float -> unit

set a n x 原地修改浮点数数组 a,将第 n 个元素替换为 x

val make : int -> float -> t

make n x 返回一个新的长度为 n 的浮点数数组,用 x 初始化。

val create : int -> t

create n 返回一个新的长度为 n 的浮点数数组,数据未初始化。

val init : int -> (int -> float) -> t

init n f 返回一个新的长度为 n 的浮点数数组,其中第 i 个元素初始化为 f i 的结果。换句话说,init n f 计算将 f 应用于整数 0n-1 的结果。

val make_matrix : int -> int -> float -> t array

make_matrix dimx dimy e 返回一个二维数组(数组的数组),第一维为 dimx,第二维为 dimy,所有元素都用 e 初始化。

val init_matrix : int -> int -> (int -> int -> float) -> t array

init_matrix dimx dimy f 返回一个二维数组(数组的数组),第一维为 dimx,第二维为 dimy,其中索引为 (x,y) 的元素用 f x y 初始化。

val append : t -> t -> t

append v1 v2 返回一个新的浮点数数组,包含浮点数数组 v1v2 的连接。

val concat : t list -> t

Float.Array.append 相同,但连接浮点数数组的列表。

val sub : t -> int -> int -> t

sub a pos len 返回一个新的长度为 len 的浮点数数组,包含浮点数数组 a 的第 pospos + len - 1 个元素。

val copy : t -> t

copy a 返回 a 的副本,即一个新的浮点数数组,包含与 a 相同的元素。

val fill : t -> int -> int -> float -> unit

fill a pos len x 原地修改浮点数数组 a,将 x 存储在第 pospos + len - 1 个元素中。

val blit : t -> int -> t -> int -> int -> unit

blit src src_pos dst dst_pos len 将浮点数数组 src 中从第 src_pos 个元素开始的 len 个元素复制到浮点数数组 dst 中,从第 dst_pos 个元素开始。即使 srcdst 是同一个浮点数数组,并且源和目标块重叠,它也能正常工作。

val to_list : t -> float list

to_list a 返回 a 中所有元素的列表。

val of_list : float list -> t

of_list l 返回一个新的浮点数数组,包含 l 中的元素。

迭代器

val iter : (float -> unit) -> t -> unit

iter f a 依次将函数 f 应用于 a 的所有元素。它等效于 f a.(0); f a.(1); ...; f a.(length a - 1); ()

val iteri : (int -> float -> unit) -> t -> unit

Float.Array.iter 相同,但函数应用于元素的索引作为第一个参数,元素本身作为第二个参数。

val map : (float -> float) -> t -> t

map f a 将函数 f 应用于 a 的所有元素,并构建一个包含 f 返回的结果的浮点数数组。

val map_inplace : (float -> float) -> t -> unit

map_inplace f a 将函数 f 应用于 a 的所有元素,并原地更新其值。

val mapi : (int -> float -> float) -> t -> t

Float.Array.map 相同,但函数应用于元素的索引作为第一个参数,元素本身作为第二个参数。

val mapi_inplace : (int -> float -> float) -> t -> unit

Float.Array.map_inplace 相同,但函数应用于元素的索引作为第一个参数,元素本身作为第二个参数。

val fold_left : ('acc -> float -> 'acc) -> 'acc -> t -> 'acc

fold_left f x init 计算 f (... (f (f x init.(0)) init.(1)) ...) init.(n-1),其中 n 是浮点数数组 init 的长度。

val fold_right : (float -> 'acc -> 'acc) -> t -> 'acc -> 'acc

fold_right f a init 计算 f a.(0) (f a.(1) ( ... (f a.(n-1) init) ...)),其中 n 是浮点数数组 a 的长度。

两个数组上的迭代器

val iter2 : (float -> float -> unit) -> t -> t -> unit

Array.iter2 f a b 将函数 f 应用于 ab 的所有元素。

val map2 : (float -> float -> float) -> t -> t -> t

map2 f a b 将函数 f 应用于 ab 的所有元素,并构建一个包含 f 返回的结果的浮点数数组:[| f a.(0) b.(0); ...; f a.(length a - 1) b.(length b - 1)|]

数组扫描

val for_all : (float -> bool) -> t -> bool

for_all f [|a1; ...; an|] 检查浮点数数组的所有元素是否都满足谓词 f。也就是说,它返回 (f a1) && (f a2) && ... && (f an)

val exists : (float -> bool) -> t -> bool

exists f [|a1; ...; an|] 检查浮点数数组中是否至少有一个元素满足谓词 f。也就是说,它返回 (f a1) || (f a2) || ... || (f an)

val mem : float -> t -> bool

mem a set 当且仅当 set 中存在一个元素在结构上等于 a 时为真,即 set 中存在一个 x 使得 compare a x = 0

val mem_ieee : float -> t -> bool

Float.Array.mem 相同,但使用 IEEE 等于而不是结构等于。

数组搜索

val find_opt : (float -> bool) -> t -> float option
val find_index : (float -> bool) -> t -> int option

find_index f a 返回 Some i,其中 i 是数组 a 中第一个满足 f x 的元素的索引,如果存在这样的元素。

如果不存在这样的元素,则返回 None

val find_map : (float -> 'a option) -> t -> 'a option
val find_mapi : (int -> float -> 'a option) -> t -> 'a option

find_map 相同,但谓词应用于元素的索引作为第一个参数(从 0 开始计数),元素本身作为第二个参数。

排序和洗牌

val sort : (float -> float -> int) -> t -> unit

根据比较函数按升序对浮点数数组进行排序。比较函数必须在其参数比较相等时返回 0,第一个参数大于时返回正整数,第一个参数小于时返回负整数(有关完整规范,请参见下文)。例如,compare 是一个合适的比较函数。调用 sort 后,数组将在原地按升序排序。sort 保证在恒定的堆空间和(最多)对数栈空间中运行。

当前实现使用堆排序。它在恒定的栈空间中运行。

比较函数的规范:设 a 为浮点数数组,cmp 为比较函数。对于 a 中的所有 xyz,以下必须为真

  • cmp x y > 0 当且仅当 cmp y x < 0
  • 如果 cmp x y >= 0 且 cmp y z >= 0,则 cmp x z >= 0

sort 返回时,a 包含与之前相同的元素,但重新排序,以便对于 a 的所有有效索引 i 和 j

  • cmp a.(i) a.(j) >= 0 当且仅当 i >= j
val stable_sort : (float -> float -> int) -> t -> unit

Float.Array.sort 相同,但排序算法是稳定的(即比较相等的元素保持其原始顺序),并且不保证在恒定的堆空间中运行。

当前实现使用归并排序。它使用一个长度为n/2的临时浮点数数组,其中n是浮点数数组的长度。它通常比Float.Array.sort的当前实现更快。

val fast_sort : (float -> float -> int) -> t -> unit

Float.Array.sortFloat.Array.stable_sort相同,选择在典型输入上速度较快的那个。

val shuffle : rand:(int -> int) -> t -> unit

shuffle rand a 使用rand随机排列a的元素。排列的分布是均匀的。

rand必须满足以下条件:调用rand n会返回范围在[0;n-1]内的均匀分布随机数。Random.int可用于此目的(不要忘记初始化生成器)。

浮点数数组和序列

val to_seq : t -> float Seq.t

以递增顺序遍历浮点数数组。迭代期间对浮点数数组的修改将反映在序列中。

val to_seqi : t -> (int * float) Seq.t

以递增顺序遍历浮点数数组,同时生成元素的索引。迭代期间对浮点数数组的修改将反映在序列中。

val of_seq : float Seq.t -> t

从生成器创建一个数组。

val map_to_array : (float -> 'a) -> t -> 'a array

map_to_array f a 将函数f应用于a的所有元素,并构建一个包含f返回结果的数组:[| f a.(0); f a.(1); ...; f a.(length a - 1) |]

val map_from_array : ('a -> float) -> 'a array -> t

map_from_array f a 将函数f应用于a的所有元素,并构建一个包含f返回结果的浮点数数组。

数组和并发安全性

当从多个域并发访问浮点数数组时,必须小心:访问数组永远不会导致程序崩溃,但未同步的访问可能会产生令人惊讶的(非顺序一致的)结果。

原子性

每个访问多个数组元素的浮点数数组操作都不是原子的。这包括迭代、扫描、排序、拆分和组合数组。

例如,考虑以下程序

let size = 100_000_000
  let a = Float.Array.make size 1.
  let update a f () =
     Float.Array.iteri (fun i x -> Float.Array.set a i (f x)) a
  let d1 = Domain.spawn (update a (fun x -> x +. 1.))
  let d2 = Domain.spawn (update a (fun x ->  2. *. x +. 1.))
  let () = Domain.join d1; Domain.join d2
  

执行此代码后,浮点数数组a的每个字段都为2.3.4.5.。如果需要原子性,则用户必须实现自己的同步(例如,使用Mutex.t)。

数据竞争

如果两个域仅访问数组的不同部分,则观察到的行为等效于这两个域的操作的某种顺序交错。

当两个域在没有同步的情况下访问同一个数组元素,并且至少一个访问是写操作时,就会发生数据竞争。在没有数据竞争的情况下,观察到的行为等效于来自不同域的操作的某种顺序交错。

只要可能,应通过使用同步来协调对数组元素的访问,从而避免数据竞争。

实际上,在存在数据竞争的情况下,程序不会崩溃,但观察到的行为可能不等于来自不同域的操作的任何顺序交错。尽管如此,即使在存在数据竞争的情况下,读取操作也会返回该位置之前某个写入操作的值,但有一些例外情况。

撕裂

在存在数据竞争的情况下,浮点数数组有两个补充注意事项。

首先,blit 操作可能会逐字节复制数组。此类 blit 操作与另一个操作之间的数据竞争可能会产生令人惊讶的值,因为撕裂:部分写入与其他操作交错可能会创建在顺序执行中不存在的浮点值。

例如,在

let zeros = Float.Array.make size 0.
  let max_floats = Float.Array.make size Float.max_float
  let res = Float.Array.copy zeros
  let d1 = Domain.spawn (fun () -> Float.Array.blit zeros 0 res 0 size)
  let d2 = Domain.spawn (fun () -> Float.Array.blit max_floats 0 res 0 size)
  let () = Domain.join d1; Domain.join d2
  

res浮点数数组可能包含既不是0.也不是max_float的值。

其次,在 32 位架构上,获取或设置字段涉及两个独立的内存访问。在存在数据竞争的情况下,用户可能会在任何操作上观察到撕裂。