module ArrayLabels:sig
..end
具有打包表示形式的浮点数数组(带标签函数)。
typet =
floatarray
具有打包表示形式的浮点数数组的类型。
val length : t -> int
返回给定浮点数数组的长度(元素数量)。
val get : t -> int -> float
get a n
返回浮点数数组 a
中的第 n
个元素。
Invalid_argument
如果 n
超出范围 0 到 (length a - 1)
。val set : t -> int -> float -> unit
set a n x
在原地修改浮点数数组 a
,用 x
替换第 n
个元素。
Invalid_argument
如果 n
超出范围 0 到 (length a - 1)
。val make : int -> float -> t
make n x
返回一个长度为 n
的新浮点数数组,用 x
初始化。
Invalid_argument
如果 n < 0
或 n > Sys.max_floatarray_length
。val create : int -> t
create n
返回一个长度为 n
的新浮点数数组,其数据未初始化。
Invalid_argument
如果 n < 0
或 n > Sys.max_floatarray_length
。val init : int -> f:(int -> float) -> t
init n ~f
返回一个长度为 n
的新浮点数数组,其第 i
个元素初始化为 f i
的结果。换句话说,init n ~f
对整数 0
到 n-1
应用 f
并建立表格。
Invalid_argument
如果 n < 0
或 n > Sys.max_floatarray_length
。val make_matrix : dimx:int -> dimy:int -> float -> t array
make_matrix ~dimx ~dimy e
返回一个二维数组(数组的数组),其第一维为 dimx
,第二维为 dimy
,其中所有元素都用 e
初始化。
Invalid_argument
如果 dimx
或 dimy
为负数或大于 Sys.max_floatarray_length
。val init_matrix : dimx:int -> dimy:int -> f:(int -> int -> float) -> t array
init_matrix ~dimx ~dimy ~f
返回一个二维数组(数组的数组),其第一维为 dimx
,第二维为 dimy
,其中索引为 (x,y
) 的元素用 f x y
初始化。
Invalid_argument
如果 dimx
或 dimy
为负数或大于 Sys.max_floatarray_length
。val append : t -> t -> t
append v1 v2
返回一个新浮点数数组,包含浮点数数组 v1
和 v2
的连接。
Invalid_argument
如果 length v1 + length v2 > Sys.max_floatarray_length
。val concat : t list -> t
与 Float.ArrayLabels.append
相同,但连接浮点数数组列表。
val sub : t -> pos:int -> len:int -> t
sub a ~pos ~len
返回一个长度为 len
的新浮点数数组,包含浮点数数组 a
中的第 pos
到 pos + len - 1
个元素。
Invalid_argument
如果 pos
和 len
未指定 a
的有效子数组;也就是说,如果 pos < 0
,或 len < 0
,或 pos + len > length a
。val copy : t -> t
copy a
返回 a
的副本,即一个包含与 a
相同元素的新浮点数数组。
val fill : t -> pos:int -> len:int -> float -> unit
fill a ~pos ~len x
在原地修改浮点数数组 a
,在第 pos
到 pos + len - 1
个元素中存储 x
。
Invalid_argument
如果 pos
和 len
未指定 a
的有效子数组。val blit : src:t ->
src_pos:int -> dst:t -> dst_pos:int -> len:int -> unit
blit ~src ~src_pos ~dst ~dst_pos ~len
将 len
个元素从浮点数数组 src
中的第 src_pos
个元素开始复制到浮点数数组 dst
中的第 dst_pos
个元素开始。即使 src
和 dst
是同一个浮点数数组,并且源和目标块重叠,它也能正常工作。
Invalid_argument
如果 src_pos
和 len
未指定 src
的有效子数组,或者如果 dst_pos
和 len
未指定 dst
的有效子数组。val to_list : t -> float list
to_list a
返回 a
中所有元素的列表。
val of_list : float list -> t
of_list l
返回一个包含 l
中元素的新浮点数数组。
Invalid_argument
如果 l
的长度大于 Sys.max_floatarray_length
。val iter : f:(float -> unit) -> t -> unit
iter ~f a
依次对 a
中的所有元素应用函数 f
。它等效于 f a.(0); f a.(1); ...; f a.(length a - 1); ()
。
val iteri : f:(int -> float -> unit) -> t -> unit
与 Float.ArrayLabels.iter
相同,但函数以元素的索引作为第一个参数,元素本身作为第二个参数应用。
val map : f:(float -> float) -> t -> t
map ~f a
将函数 f
应用于 a
中的所有元素,并使用 f
返回的结果构建一个浮点数数组。
val map_inplace : f:(float -> float) -> t -> unit
map_inplace f a
将函数 f
应用于 a
中的所有元素,并更新它们的值,在原地进行。
val mapi : f:(int -> float -> float) -> t -> t
与 Float.ArrayLabels.map
相同,但函数以元素的索引作为第一个参数,元素本身作为第二个参数应用。
val mapi_inplace : f:(int -> float -> float) -> t -> unit
与 Float.ArrayLabels.map_inplace
相同,但函数以元素的索引作为第一个参数,元素本身作为第二个参数应用。
val fold_left : f:('acc -> float -> 'acc) -> init:'acc -> t -> 'acc
fold_left ~f x ~init
计算 f (... (f (f x init.(0)) init.(1)) ...) init.(n-1)
,其中 n
是浮点数数组 init
的长度。
val fold_right : f:(float -> 'acc -> 'acc) -> t -> init:'acc -> 'acc
fold_right f a init
计算 f a.(0) (f a.(1) ( ... (f a.(n-1) init) ...))
,其中 n
是浮点数数组 a
的长度。
val iter2 : f:(float -> float -> unit) ->
t -> t -> unit
Array.iter2 ~f a b
对 a
和 b
中的所有元素应用函数 f
。
Invalid_argument
如果浮点数数组大小不同。val map2 : f:(float -> float -> float) ->
t -> t -> t
map2 ~f a b
对 a
和 b
中的所有元素应用函数 f
,并使用 f
返回的结果构建一个浮点数数组:[| f a.(0) b.(0); ...; f a.(length a - 1) b.(length b - 1)|]
。
Invalid_argument
如果浮点数数组大小不同。val for_all : f:(float -> bool) -> t -> bool
for_all ~f [|a1; ...; an|]
检查浮点数数组中所有元素是否都满足谓词 f
。也就是说,它返回 (f a1) && (f a2) && ... && (f an)
。
val exists : f:(float -> bool) -> t -> bool
exists f [|a1; ...; an|]
检查浮点数数组中至少一个元素是否满足谓词 f
。也就是说,它返回 (f a1) || (f a2) || ... || (f an)
。
val mem : float -> set:t -> bool
mem a ~set
为真,当且仅当 set
中存在一个元素在结构上等于 a
,即 set
中存在一个 x
使得 compare a x = 0
。
val mem_ieee : float -> set:t -> bool
与 Float.ArrayLabels.mem
相同,但使用 IEEE 等于,而不是结构等于。
val find_opt : f:(float -> bool) -> t -> float option
val find_index : f:(float -> bool) -> t -> int option
find_index ~f a
返回 Some i
,其中 i
是数组 a
中第一个满足 f x
的元素的索引,如果有这样的元素。
如果不存在这样的元素,它将返回 None
。
val find_map : f:(float -> 'a option) -> t -> 'a option
val find_mapi : f:(int -> float -> 'a option) -> t -> 'a option
与 find_map
相同,但谓词以元素的索引作为第一个参数(从 0 开始计数),元素本身作为第二个参数应用。
val sort : cmp:(float -> float -> int) -> t -> unit
根据比较函数对浮点数数组进行升序排序。比较函数必须在它的参数比较相等时返回 0,第一个参数更大时返回正整数,第一个参数更小时返回负整数(有关完整的规范,请参见下文)。例如,compare
是一个合适的比较函数。调用 sort
后,数组将按升序在原地排序。sort
保证以恒定堆空间和(最多)对数堆栈空间运行。
当前实现使用堆排序。它以恒定堆栈空间运行。
比较函数的规范:设 a
为浮点数数组,cmp
为比较函数。以下必须对 a
中的所有 x
、y
、z
成立
cmp x y
> 0 当且仅当 cmp y x
< 0cmp x y
>= 0 且 cmp y z
>= 0,则 cmp x z
>= 0当 sort
返回时,a
包含与之前相同的元素,但重新排序,以便对于 a
的所有有效索引 i 和 j
cmp a.(i) a.(j)
>= 0 当且仅当 i >= jval stable_sort : cmp:(float -> float -> int) -> t -> unit
与 Float.ArrayLabels.sort
相同,但排序算法是稳定的(即比较相等的元素保持其原始顺序),并且不保证以恒定堆空间运行。
当前实现使用归并排序。它使用长度为 n/2
的临时浮点数数组,其中 n
是浮点数数组的长度。它通常比 Float.ArrayLabels.sort
的当前实现更快。
val fast_sort : cmp:(float -> float -> int) -> t -> unit
与 Float.ArrayLabels.sort
或 Float.ArrayLabels.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 : f:(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 : f:('a -> float) -> 'a array -> t
map_from_array ~f a
将函数 f
应用于 a
的所有元素,并使用 f
返回的结果构建浮点数组。
在从多个域并发访问浮点数组时,必须小心:访问数组永远不会导致程序崩溃,但不同步的访问可能会产生令人惊讶的(非顺序一致的)结果。
访问多个数组元素的每个浮点数组操作都不是原子的。这包括迭代、扫描、排序、拆分和合并数组。
例如,考虑以下程序
let size = 100_000_000
let a = Float.ArrayLabels.make size 1.
let update a f () =
Float.ArrayLabels.iteri ~f:(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 位架构上,获取或设置字段涉及两个独立的内存访问。在存在数据竞争的情况下,用户可能会观察到对任何操作的撕裂。