数组是连续数据元素的固定大小、从零开始的可变集合,这些集合都是同一类型的。
创建数组
可以通过多种方式创建数组。 可以通过列出连续值以及[|
|]
用分号分隔来创建一个小数组,如以下示例所示。
let array1 = [| 1; 2; 3 |]
还可以将每个元素放在单独的行上,在这种情况下,分号分隔符是可选的。
let array1 =
[|
1
2
3
|]
数组元素的类型是从所使用的文本推断的,并且必须一致。
// This is an array of 3 integers.
let array1 = [| 1; 2; 3 |]
// This is an array of a tuple of 3 integers.
let array2 = [| 1, 2, 3 |]
以下代码导致错误,因为 3.0 是浮点数,1 和 2 是整数。
// Causes an error. The 3.0 (float) cannot be converted to integer implicitly.
// let array3 = [| 1; 2; 3.0 |]
以下代码也会导致错误,因为 1,2
元组是 3
整数。
// Causes an error too. The 3 (integer) cannot be converted to tuple implicitly.
// let array4 = [| 1, 2; 3 |]
还可以使用序列表达式创建数组。 下面是一个示例,它创建一个由 1 到 10 的整数的平方数组。
let array3 = [| for i in 1 .. 10 -> i * i |]
若要创建一个数组,其中所有元素初始化为零,请使用 Array.zeroCreate
。
let arrayOfTenZeroes : int array = Array.zeroCreate 10
Access 元素
可以使用方括号([
和 ]
)访问数组元素。 原始点语法(.[index]
)仍受支持,但不再建议从 F# 6.0 开始。
array1[0]
数组索引从 0 开始。
还可以通过使用切片表示法来访问数组元素,这使你可以指定数组的子范围。 切片表示法的示例如下。
// Accesses elements from 0 to 2.
array1[0..2]
// Accesses elements from the beginning of the array to 2.
array1[..2]
// Accesses elements from 2 to the end of the array.
array1[2..]
使用切片表示法时,将创建数组的新副本。
数组类型和模块
所有 F# 数组的类型都是 .NET Framework 类型 System.Array。 因此,F# 数组支持所有可用的 System.Array功能。
该 Array
模块 支持对一维数组执行的作。 这些模块Array2D
Array3D
和Array4D
包含分别支持对两个、三个和四个维度的数组执行的作的函数。 可以使用 创建排名大于四 System.Array的数组。
简单函数
Array.get
获取元素。
Array.length
提供数组的长度。
Array.set
将元素设置为指定的值。 下面的代码示例演示了这些函数的使用。
let array1 = Array.create 10 ""
for i in 0 .. array1.Length - 1 do
Array.set array1 i (i.ToString())
for i in 0 .. array1.Length - 1 do
printf "%s " (Array.get array1 i)
输出如下所示。
0 1 2 3 4 5 6 7 8 9
创建数组的函数
多个函数无需现有数组即可创建数组。
Array.empty
创建不包含任何元素的新数组。
Array.create
创建指定大小的数组,并将所有元素设置为提供的值。
Array.init
创建一个数组,给定一个维度和一个用于生成元素的函数。
Array.zeroCreate
创建一个数组,其中所有元素都初始化为数组类型的零值。 以下代码演示了这些函数。
let myEmptyArray = Array.empty
printfn "Length of empty array: %d" myEmptyArray.Length
printfn "Array of floats set to 5.0: %A" (Array.create 10 5.0)
printfn "Array of squares: %A" (Array.init 10 (fun index -> index * index))
let (myZeroArray : float array) = Array.zeroCreate 10
输出如下所示。
Length of empty array: 0
Area of floats set to 5.0: [|5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0|]
Array of squares: [|0; 1; 4; 9; 16; 25; 36; 49; 64; 81|]
Array.copy
创建一个新数组,其中包含从现有数组复制的元素。 请注意,副本是浅表副本,这意味着如果元素类型是引用类型,则只复制引用,而不是基础对象。 下面的代码示例对此进行了说明。
open System.Text
let firstArray : StringBuilder array = Array.init 3 (fun index -> new StringBuilder(""))
let secondArray = Array.copy firstArray
// Reset an element of the first array to a new value.
firstArray[0] <- new StringBuilder("Test1")
// Change an element of the first array.
firstArray[1].Insert(0, "Test2") |> ignore
printfn "%A" firstArray
printfn "%A" secondArray
上述代码的输出如下所示:
[|Test1; Test2; |]
[|; Test2; |]
该字符串 Test1
仅出现在第一个数组中,因为创建新元素的作将覆盖引用, firstArray
但不会影响对仍然存在 secondArray
的空字符串的原始引用。 字符串 Test2
出现在这两个数组中,因为 Insert
类型上的 System.Text.StringBuilder 作会影响基础 System.Text.StringBuilder 对象,这两个数组中都引用了该对象。
Array.sub
从数组的子范围生成一个新数组。 通过提供起始索引和长度来指定子范围。 以下代码演示了如何使用 Array.sub
。
let a1 = [| 0 .. 99 |]
let a2 = Array.sub a1 5 10
printfn "%A" a2
输出显示子数组从元素 5 开始,包含 10 个元素。
[|5; 6; 7; 8; 9; 10; 11; 12; 13; 14|]
Array.append
通过组合两个现有数组来创建新数组。
以下代码演示 Array.append。
printfn "%A" (Array.append [| 1; 2; 3|] [| 4; 5; 6|])
上述代码的输出如下所示。
[|1; 2; 3; 4; 5; 6|]
Array.choose
选择要包含在新数组中的数组的元素。 以下代码演示 Array.choose
了 。 请注意,数组的元素类型不必与选项类型中返回的值的类型匹配。 在此示例中,元素类型为 int
,选项是多项式函数的结果, elem*elem - 1
作为浮点数。
printfn "%A" (Array.choose (fun elem -> if elem % 2 = 0 then
Some(float (elem*elem - 1))
else
None) [| 1 .. 10 |])
上述代码的输出如下所示。
[|3.0; 15.0; 35.0; 63.0; 99.0|]
Array.collect
对现有数组的每个数组元素运行指定的函数,然后收集函数生成的元素,并将其合并到新的数组中。 以下代码演示 Array.collect
了 。
printfn "%A" (Array.collect (fun elem -> [| 0 .. elem |]) [| 1; 5; 10|])
上述代码的输出如下所示。
[|0; 1; 0; 1; 2; 3; 4; 5; 0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10|]
Array.concat
采用数组序列并将其合并为单个数组。 以下代码演示 Array.concat
了 。
Array.concat [ [|0..3|] ; [|4|] ]
//output [|0; 1; 2; 3; 4|]
Array.concat [| [|0..3|] ; [|4|] |]
//output [|0; 1; 2; 3; 4|]
Array.filter
获取布尔条件函数并生成一个新数组,该数组仅包含条件为 true 的输入数组中的元素。 以下代码演示 Array.filter
了 。
printfn "%A" (Array.filter (fun elem -> elem % 2 = 0) [| 1 .. 10|])
上述代码的输出如下所示。
[|2; 4; 6; 8; 10|]
Array.rev
通过反转现有数组的顺序来生成新数组。 以下代码演示 Array.rev
了 。
let stringReverse (s: string) =
System.String(Array.rev (s.ToCharArray()))
printfn "%A" (stringReverse("!dlrow olleH"))
上述代码的输出如下所示。
"Hello world!"
可以使用管道运算符(|>
如以下示例中所示)轻松组合数组模块中的函数来转换数组。
[| 1 .. 10 |]
|> Array.filter (fun elem -> elem % 2 = 0)
|> Array.choose (fun elem -> if (elem <> 8) then Some(elem*elem) else None)
|> Array.rev
|> printfn "%A"
输出为
[|100; 36; 16; 4|]
多维数组
可以创建多维数组,但没有用于编写多维数组文本的语法。 使用运算符 array2D
根据数组元素序列创建数组。 序列可以是数组或列表文本。 例如,以下代码创建二维数组。
let my2DArray = array2D [ [ 1; 0]; [0; 1] ]
还可以使用该函数 Array2D.init
初始化两个维度的数组,并且类似的函数可用于三维和四个维度的数组。 这些函数采用用于创建元素的函数。 若要创建包含设置为初始值的元素的二维数组,而不是指定函数,请使用 Array2D.create
该函数,该函数也可用于最多四个维度的数组。 下面的代码示例首先演示如何创建包含所需元素的数组数组,然后使用 Array2D.init
该数组生成所需的二维数组。
let arrayOfArrays = [| [| 1.0; 0.0 |]; [|0.0; 1.0 |] |]
let twoDimensionalArray = Array2D.init 2 2 (fun i j -> arrayOfArrays[i][j])
数组支持数组索引和切片语法,其排名为 4。 在多个维度中指定索引时,可以使用逗号分隔索引,如以下代码示例所示。
twoDimensionalArray[0, 1] <- 1.0
二维数组的类型将写出为<type>[,]
(例如int[,]
double[,]
,),而三维数组的类型将写入<type>[,,]
为更高维度的数组,依此类写。
只有可用于一维数组的函数的子集也可用于多维数组。
数组切片和多维数组
在二维数组(矩阵)中,可以通过指定范围并使用通配符(*
)字符指定整个行或列来提取子矩阵。
// Get rows 1 to N from an NxM matrix (returns a matrix):
matrix[1.., *]
// Get rows 1 to 3 from a matrix (returns a matrix):
matrix[1..3, *]
// Get columns 1 to 3 from a matrix (returns a matrix):
matrix[*, 1..3]
// Get a 3x3 submatrix:
matrix[1..3, 1..3]
可以将多维数组分解为相同或更低维度的子数组。 例如,可以通过指定单个行或列从矩阵获取向量。
// Get row 3 from a matrix as a vector:
matrix[3, *]
// Get column 3 from a matrix as a vector:
matrix[*, 3]
可以将此切片语法用于实现元素访问运算符和重载 GetSlice
方法的类型。 例如,下面的代码创建一个矩阵类型来包装 F# 2D 数组,实现 Item 属性以支持数组索引,并实现三个 GetSlice
版本的数组。 如果可将此代码用作矩阵类型的模板,则可以使用本节描述的所有切片作。
type Matrix<'T>(N: int, M: int) =
let internalArray = Array2D.zeroCreate<'T> N M
member this.Item
with get(a: int, b: int) = internalArray[a, b]
and set(a: int, b: int) (value:'T) = internalArray[a, b] <- value
member this.GetSlice(rowStart: int option, rowFinish : int option, colStart: int option, colFinish : int option) =
let rowStart =
match rowStart with
| Some(v) -> v
| None -> 0
let rowFinish =
match rowFinish with
| Some(v) -> v
| None -> internalArray.GetLength(0) - 1
let colStart =
match colStart with
| Some(v) -> v
| None -> 0
let colFinish =
match colFinish with
| Some(v) -> v
| None -> internalArray.GetLength(1) - 1
internalArray[rowStart..rowFinish, colStart..colFinish]
member this.GetSlice(row: int, colStart: int option, colFinish: int option) =
let colStart =
match colStart with
| Some(v) -> v
| None -> 0
let colFinish =
match colFinish with
| Some(v) -> v
| None -> internalArray.GetLength(1) - 1
internalArray[row, colStart..colFinish]
member this.GetSlice(rowStart: int option, rowFinish: int option, col: int) =
let rowStart =
match rowStart with
| Some(v) -> v
| None -> 0
let rowFinish =
match rowFinish with
| Some(v) -> v
| None -> internalArray.GetLength(0) - 1
internalArray[rowStart..rowFinish, col]
module test =
let generateTestMatrix x y =
let matrix = new Matrix<float>(3, 3)
for i in 0..2 do
for j in 0..2 do
matrix[i, j] <- float(i) * x - float(j) * y
matrix
let test1 = generateTestMatrix 2.3 1.1
let submatrix = test1[0..1, 0..1]
printfn $"{submatrix}"
let firstRow = test1[0,*]
let secondRow = test1[1,*]
let firstCol = test1[*,0]
printfn $"{firstCol}"
数组上的布尔函数
一个或两个数组中的函数 Array.exists
和 Array.exists2
测试元素分别。 这些函数采用测试函数并返回 true
满足条件的元素(或元素对 Array.exists2
)。
以下代码演示了 Array.exists
如何使用和 Array.exists2
。 在这些示例中,新函数仅应用其中一个参数(在这些情况下是函数参数)来创建。
let allNegative = Array.exists (fun elem -> abs (elem) = elem) >> not
printfn "%A" (allNegative [| -1; -2; -3 |])
printfn "%A" (allNegative [| -10; -1; 5 |])
printfn "%A" (allNegative [| 0 |])
let haveEqualElement = Array.exists2 (fun elem1 elem2 -> elem1 = elem2)
printfn "%A" (haveEqualElement [| 1; 2; 3 |] [| 3; 2; 1|])
上述代码的输出如下所示。
true
false
false
true
同样,该函数 Array.forall
测试数组以确定每个元素是否满足布尔条件。 该变体 Array.forall2
使用一个布尔函数执行相同的作,该函数涉及两个长度相等数组的元素。 以下代码演示了这些函数的使用。
let allPositive = Array.forall (fun elem -> elem > 0)
printfn "%A" (allPositive [| 0; 1; 2; 3 |])
printfn "%A" (allPositive [| 1; 2; 3 |])
let allEqual = Array.forall2 (fun elem1 elem2 -> elem1 = elem2)
printfn "%A" (allEqual [| 1; 2 |] [| 1; 2 |])
printfn "%A" (allEqual [| 1; 2 |] [| 2; 1 |])
这些示例的输出如下所示。
false
true
true
false
搜索数组
Array.find
获取布尔函数并返回函数返回 true
的第一个元素,或者如果未找到满足条件的元素,则引发 System.Collections.Generic.KeyNotFoundException 该元素。
Array.findIndex
与 类似 Array.find
,只不过它返回元素的索引而不是元素本身。
以下代码使用 Array.find
并 Array.findIndex
查找既是完美方块又完美多维数据集的数字。
let arrayA = [| 2 .. 100 |]
let delta = 1.0e-10
let isPerfectSquare (x:int) =
let y = sqrt (float x)
abs(y - round y) < delta
let isPerfectCube (x:int) =
let y = System.Math.Pow(float x, 1.0/3.0)
abs(y - round y) < delta
let element = Array.find (fun elem -> isPerfectSquare elem && isPerfectCube elem) arrayA
let index = Array.findIndex (fun elem -> isPerfectSquare elem && isPerfectCube elem) arrayA
printfn "The first element that is both a square and a cube is %d and its index is %d." element index
输出如下所示。
The first element that is both a square and a cube is 64 and its index is 62.
Array.tryFind
与 类似 Array.find
,只是其结果为选项类型,如果未找到任何元素,则返回 None
该类型。
Array.tryFind
在不知道匹配元素是否位于数组中时,应使用 Array.find
。 同样, Array.tryFindIndex
除了 Array.findIndex
选项类型是返回值之外。 如果未找到任何元素,则选项为 None
.
以下代码演示了如何使用 Array.tryFind
。 此代码取决于前面的代码。
let delta = 1.0e-10
let isPerfectSquare (x:int) =
let y = sqrt (float x)
abs(y - round y) < delta
let isPerfectCube (x:int) =
let y = System.Math.Pow(float x, 1.0/3.0)
abs(y - round y) < delta
let lookForCubeAndSquare array1 =
let result = Array.tryFind (fun elem -> isPerfectSquare elem && isPerfectCube elem) array1
match result with
| Some x -> printfn "Found an element: %d" x
| None -> printfn "Failed to find a matching element."
lookForCubeAndSquare [| 1 .. 10 |]
lookForCubeAndSquare [| 100 .. 1000 |]
lookForCubeAndSquare [| 2 .. 50 |]
输出如下所示。
Found an element: 1
Found an element: 729
Failed to find a matching element.
除了查找元素以外,还需要转换元素时使用 Array.tryPick
。 结果是函数将转换后的元素作为选项值返回的第一个元素,如果未 None
找到此类元素,则返回该元素。
以下代码演示了 Array.tryPick
如何使用 。 在这种情况下,定义了多个本地帮助程序函数,而不是 lambda 表达式,以简化代码。
let findPerfectSquareAndCube array1 =
let delta = 1.0e-10
let isPerfectSquare (x:int) =
let y = sqrt (float x)
abs(y - round y) < delta
let isPerfectCube (x:int) =
let y = System.Math.Pow(float x, 1.0/3.0)
abs(y - round y) < delta
// intFunction : (float -> float) -> int -> int
// Allows the use of a floating point function with integers.
let intFunction function1 number = int (round (function1 (float number)))
let cubeRoot x = System.Math.Pow(x, 1.0/3.0)
// testElement: int -> (int * int * int) option
// Test an element to see whether it is a perfect square and a perfect
// cube, and, if so, return the element, square root, and cube root
// as an option value. Otherwise, return None.
let testElement elem =
if isPerfectSquare elem && isPerfectCube elem then
Some(elem, intFunction sqrt elem, intFunction cubeRoot elem)
else None
match Array.tryPick testElement array1 with
| Some (n, sqrt, cuberoot) -> printfn "Found an element %d with square root %d and cube root %d." n sqrt cuberoot
| None -> printfn "Did not find an element that is both a perfect square and a perfect cube."
findPerfectSquareAndCube [| 1 .. 10 |]
findPerfectSquareAndCube [| 2 .. 100 |]
findPerfectSquareAndCube [| 100 .. 1000 |]
findPerfectSquareAndCube [| 1000 .. 10000 |]
findPerfectSquareAndCube [| 2 .. 50 |]
输出如下所示。
Found an element 1 with square root 1 and cube root 1.
Found an element 64 with square root 8 and cube root 4.
Found an element 729 with square root 27 and cube root 9.
Found an element 4096 with square root 64 and cube root 16.
Did not find an element that is both a perfect square and a perfect cube.
对数组执行计算
该 Array.average
函数返回数组中每个元素的平均值。 它仅限于支持按整数进行精确除法的元素类型,包括浮点类型,但不包括整型类型。 该 Array.averageBy
函数返回在每个元素上调用函数的结果的平均值。 对于整型数组,可以使用 Array.averageBy
函数并将每个元素转换为计算的浮点类型。
使用 Array.max
或 Array.min
获取最大或最小元素(如果元素类型支持它)。 同样, Array.maxBy
并允许 Array.minBy
首先执行函数,或许可以转换为支持比较的类型。
Array.sum
添加数组的元素,并在 Array.sumBy
每个元素上调用一个函数,并将结果一起添加。
若要在不存储返回值的情况下对数组中的每个元素执行函数,请使用 Array.iter
。 对于涉及两个长度相等数组的函数,请使用 Array.iter2
。 如果还需要保留函数结果的数组,请使用 Array.map
或 Array.map2
一次对两个数组进行作。
变体 Array.iteri
并允许 Array.iteri2
在计算中涉及元素的索引;同样适用于 Array.mapi
和 Array.mapi2
。
涉及数组的所有元素的函数Array.fold
、Array.foldBack
、Array.reduceBack
Array.reduce
、和Array.scanBack
Array.scan
执行算法。 同样,变体 Array.fold2
对 Array.foldBack2
两个数组执行计算。
这些用于执行计算的函数对应于 List 模块中同名的函数。 有关用法示例,请参阅 列表。
修改数组
Array.set
将元素设置为指定的值。
Array.fill
将数组中的元素范围设置为指定值。 下面的代码提供了一个示例 Array.fill
。
let arrayFill1 = [| 1 .. 25 |]
Array.fill arrayFill1 2 20 0
printfn "%A" arrayFill1
输出如下所示。
[|1; 2; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 23; 24; 25|]
可用于 Array.blit
将一个数组的子部分复制到另一个数组。
转换为和从其他类型转换
Array.ofList
从列表中创建数组。
Array.ofSeq
从序列创建数组。
Array.toList
然后 Array.toSeq
从数组类型转换为这些其他集合类型。
对数组进行排序
用于 Array.sort
使用泛型比较函数对数组进行排序。 用于 Array.sortBy
指定生成值(称为 键)的函数,以便通过使用键上的泛型比较函数进行排序。 如果要提供自定义比较函数,请使用 Array.sortWith
。
Array.sort
、 Array.sortBy
和 Array.sortWith
所有将排序的数组作为新数组返回。
Array.sortInPlace
变体,Array.sortInPlaceBy
并Array.sortInPlaceWith
修改现有数组,而不是返回新数组。
数组和元组
Array.zip
函数并将Array.unzip
元组对的数组转换为数组的元组,反之亦然。
Array.zip3
与 Array.unzip3
三个数组的元组相同,不同之处在于它们与三个数组的三个元素或元组一起使用。
数组上的并行计算
该模块 Array.Parallel
包含用于对数组执行并行计算的函数。 此模块在面向版本 4 之前的 .NET Framework 版本的应用程序中不可用。