ZetCode

F# 数组

最后修改时间 2025 年 5 月 1 日

在本文中,我们将介绍如何在 F# 中处理数组。

一个数组是固定大小的、零基准的、可变的、由连续元素组成的集合。元素的数据类型相同。

let vals = [| 1; 2; 3; 4; 5 |]

可以使用数组字面量创建数组。数组字面量由用分号分隔的元素组成,位于 [||] 之间。

let vals = [|
    1
    2
    3
    4
    5
|]

在另一种语法中,分号是可选的。

F# 数组简单示例

下面是一个简单的数组示例。

main.fsx
let vals = [| 1; 2; 3; 4; 5; 6 |]

printfn "%A" vals
printfn "%d" vals.Length

我们有一个整数数组。我们打印数组的内容,然后打印数组的大小。

let vals = [| 1; 2; 3; 4; 5; 6 |]

我们使用数组字面量定义了一个新的整数数组。

printfn "%A" vals

使用 %A 格式说明符,我们可以美观地打印数组。

printfn "%d" vals.Length

我们使用 Length 获取数组的大小。

λ dotnet fsi main.fsx
[|1; 2; 3; 4; 5; 6|]
6

F# 数组头部和尾部

在下一个示例中,我们获取数组的头部和尾部。

main.fsx
let vals = [| 1; 2; 3; 4; 5; 6 |]

printfn "%d" (Array.head vals)
printfn "%A" (Array.tail vals)

我们打印整数数组的头部和尾部。

printfn "%d" (Array.head vals)

Array.head 打印数组的第一个元素。

printfn "%A" (Array.tail vals)

使用 Array.tail 函数,我们可以获取给定数组中除第一个元素之外的所有元素(尾部)。

λ dotnet fsi main.fsx
[|2; 3; 4; 5; 6|]
1
[|2; 3; 4; 5; 6|]

F# 数组迭代

在下一个示例中,我们遍历数组的元素。

main.fsx
let vals = [| 1; 2; 3; 4; 5; 6 |]

vals |> Array.iter (printfn "%d")

printfn "------------------------"

for e in vals do
    printfn "%d" e

数组迭代有两种基本方式。

vals |> Array.iter (printfn "%d")

Array.iter 是遍历数组元素的函数式方法。

for e in vals do
    printfn "%d" e

经典、命令式的方法是通过 for 循环。

λ dotnet fsi main.fsx
1
2
3
4
5
6
------------------------
1
2
3
4
5
6

F# 数组索引

通过索引访问数组元素。

main.fsx
let words = [| "pen"; "cup"; "dog"; "person";
    "cement"; "coal"; "spectacles"; "cup"; "bread" |]

let w1 = Array.item 1 words
printfn "%s" w1

let w2 = words[0]
printfn "%s" w2

let i1 = Array.findIndex(fun e -> e = "cup") words
printfn $"The first index of cup is {i1}"

let i2 = Array.findIndexBack(fun e -> e = "cup") words
printfn $"The last index of cup is {i2}"

程序包含数组索引操作。

let w1 = Array.item 1 words

我们使用 Array.item 获取数组的第二个元素。

let w2 = words[0]

我们也可以使用经典的 C 风格语法。

let i1 = Array.findIndex(fun e -> e = "cup") words

使用 Array.findIndex,我们可以找到满足给定谓词函数的第一个元素。

let i2 = Array.findIndexBack(fun e -> e = "cup") words

使用 Array.findIndexBack,我们可以找到满足给定谓词函数的最后一个元素。

λ dotnet fsi main.fsx
cup
pen
The first index of cup is 1
The last index of cup is 7

F# 数组修改元素

我们可以使用 <-Array.set 修改数组元素。

main.fsx
let words = [| "pen"; "cup"; "dog"; "person";
    "cement"; "coal"; "spectacles"; "cup"; "bread" |]

printfn "%A" words

words[0] <- "pencil"
Array.set words 6 "specs"

printfn "%A" words

我们有一个字符串数组。我们修改了第一个和第六个元素。

λ dotnet fsi main.fsx
[|"pen"; "cup"; ... "coal"; "spectacles"; "cup"; "bread"|]
[|"pencil"; "cup"; ... "coal"; "specs"; "cup"; "bread"|]

F# 二维数组

可以使用 array2D 操作符创建二维数组。

main.fsx
let vals = array2D [ [ 1; 2; 3]; [4; 5; 6]; [7; 8; 9] ]

printfn "%A" vals
printfn "%d" vals[0, 0]
printfn "%A" vals[0, 0..2]
printfn "%A" vals[0..2, 0]

我们创建一个二维整数数组。

printfn "%d" vals[0, 0]

我们获取第一行的第一个元素。

printfn "%d" vals[1, 2]

我们获取第二行的第三个元素。

printfn "%A" vals[0, 0..2]

我们获取第一行。

printfn "%A" vals[0..2, 0]

我们获取第一列。

λ dotnet fsi main.fsx
[[1; 2; 3]
 [4; 5; 6]
 [7; 8; 9]]
1
6
[|1; 2; 3|]
[|1; 4; 7|]

F# Array.map

Array.map 函数将给定函数应用于集合的每个元素。

main.fsx
let vals = [| 1..10 |]

let res = Array.map(fun e -> e * 2) vals
printfn "%A" res

我们在整数数组上应用了一个 map 函数。

let vals = [| 1..10 |]

我们使用范围运算符定义了一个数组。

let res = Array.map(fun e -> e * 2) vals

数组的每个元素都乘以 2。结果被赋给 res 变量。

λ dotnet fsi main.fsx
[|2; 4; 6; 8; 10; 12; 14; 16; 18; 20|]

F# Array.filter

我们可以使用 Array.filter 过滤数组元素。

main.fsx
let vals = [| -3; -2; 0; 1; -5; 7; 9 |]
let words = [| "sky"; "war"; "rock"; "ocean"; "cloud"; "water" |]

let pos = Array.filter(fun e -> e > 0) vals
printfn "%A" pos

let res = Array.filter(fun (e:string) -> e.StartsWith("w")) words
printfn "%A" res

在程序中,我们找出整数数组中的所有正数,以及单词数组中所有以“w”开头的字符串。

let pos = Array.filter(fun e -> e > 0) vals

Array.filter 函数接受一个谓词函数。所有元素都必须满足给定的谓词。

let res = Array.filter(fun (e:string) -> e.StartsWith("w")) words

有时,有必要通过显式类型定义来帮助编译器。

λ dotnet fsi main.fsx
[1; 7; 9]
["war"; "water"]

F# 合并数组

可以使用 Array.concat 或数组推导式合并数组。Array.unique 返回一个不包含重复项的数组。

main.fsx
let a = [| 1; 2; 3; 4 |]
let b = [| 4; 4; 5; 6 |]

let d = Array.concat [a; b] 
let c = Array.distinct d

let e = [| yield! a; yield! b |]
let f = Array.distinct e

printfn "%A" c
printfn "%A" d
printfn "%A" e
printfn "%A" f

程序合并了两个数组。

let d = Array.concat [a; b]

我们使用 Array.concat 合并了两个数组。

let c = Array.distinct d

使用 Array.distinct 删除了重复项。

let e = [| yield! a; yield! b |]

yield! 将另一个序列的所有项插入正在构建的此序列中。

λ dotnet fsi main.fsx
[|1; 2; 3; 4; 5; 6|]
[|1; 2; 3; 4; 4; 4; 5; 6|]
[|1; 2; 3; 4; 4; 4; 5; 6|]
[|1; 2; 3; 4; 5; 6|]

F# 排序整数数组

在下一个示例中,我们对整数进行排序。

main.fsx
let nums = [| -1; 6; -2; 3; 0; -4; 5; 1; 2 |]

nums |> Array.sort |> printfn "%A"
nums |> Array.sortDescending |> printfn "%A"

nums |> Array.sortBy (abs) |> printfn "%A"
nums |> Array.sortByDescending (abs) |> printfn "%A"

我们有一个整数数组。我们使用 Array.sortArray.sortDescending 对它们进行排序。

nums |> Array.sortBy (abs) |> printfn "%A"
nums |> Array.sortByDescending (abs) |> printfn "%A"

abs 的帮助下,我们对整数进行排序,而不考虑它们的符号。

λ dotnet fsi main.fsx
[|-4; -2; -1; 0; 1; 2; 3; 5; 6|]
[|6; 5; 3; 2; 1; 0; -1; -2; -4|]
[|0; -1; 1; -2; 2; 3; -4; 5; 6|]
[|6; 5; -4; 3; -2; 2; -1; 1; 0|]

F# 排序记录数组

在下一个示例中,我们对记录数组进行排序。

main.fsx
type User =
    { Name: string
      Occupation: string
      Salary: int }

let users =
    [| { Name = "John Doe"
         Occupation = "gardener"
         Salary = 1280 }
       { Name = "Roger Roe"
         Occupation = "driver"
         Salary = 860 }
       { Name = "Tom Brown"
         Occupation = "shopkeeper"
         Salary = 990 } |]

users
|> Array.sortBy (fun u -> u.Salary)
|> Array.iter (fun u -> printfn "%A" u)

printfn "--------------------------------"

users
|> Array.sortByDescending (fun u -> u.Occupation)
|> Array.iter (fun u -> printfn "%A" u)

程序包含一个 User 记录数组。我们按工资和职业对用户进行排序。

users
|> Array.sortBy (fun u -> u.Salary)
|> Array.iter (fun u -> printfn "%A" u)

使用 Array.sortBy 按工资升序对用户进行排序。

users
|> Array.sortByDescending (fun u -> u.Occupation)
|> Array.iter (fun u -> printfn "%A" u)

在这里,使用 sortByDescending 按职业降序对用户进行排序。

λ dotnet fsi main.fsx
{ Name = "Roger Roe"
  Occupation = "driver"
  Salary = 860 }
{ Name = "Tom Brown"
  Occupation = "shopkeeper"
  Salary = 990 }
{ Name = "John Doe"
  Occupation = "gardener"
  Salary = 1280 }
--------------------------------
{ Name = "Tom Brown"
  Occupation = "shopkeeper"
  Salary = 990 }
{ Name = "John Doe"
  Occupation = "gardener"
  Salary = 1280 }
{ Name = "Roger Roe"
  Occupation = "driver"
  Salary = 860 }

F# 数组推导式

数组推导式是生成数组的强大语法。数组推导式提供了一种创建数组的简洁方法。

在 F# 中,我们可以使用范围和生成器创建数组推导式。

main.fsx
let vals = [| -1; 0; 2; -2; 1; 3; 4; -6 |]

let pos =
    [| for e in vals do
          if e > 0 then yield e |]

printfn "%A" pos

printfn "---------------------------------"

[| for e in 1 .. 100 -> e * e |] |> printfn "%A"


printfn "---------------------------------"

[| for a in 1 .. 100 do
    if a % 3 = 0 && a % 5 = 0 then yield a|] |> printfn "%A"

printfn "---------------------------------"

let vals3 =
    [| for x in 1 .. 3 do
          for y in 1 .. 10 -> x, y |]

printfn "%A" vals3

在 F# 中,数组推导式使用 for 循环、if 条件和 yield 关键字。

let vals = [| -1; 0; 2; -2; 1; 3; 4; -6 |]

let pos =
    [| for e in vals do
          if e > 0 then yield e |]

我们有一个值数组。使用数组推导式构造了一个新数组。它只包含正值。

[| for e in 1 .. 100 -> e * e |] |> printfn "%A"

我们可以在数组推导式中使用范围。

[| for a in 1 .. 100 do
    if a % 3 = 0 && a % 5 = 0 then yield a|] |> printfn "%A"

这里我们使用了两个 if 条件。

let vals3 =
    [| for x in 1 .. 3 do
          for y in 1 .. 10 -> x, y |]

此外,还可以使用两个 for 循环。

λ dotnet fsi main.fsx
[2; 1; 3; 4]
---------------------------------
[1; 4; 9; 16; 25; 36; 49; 64; 81; 100; 121; 144; 169; 196; 225; 256; 289; 324;
 361; 400; 441; 484; 529; 576; 625; 676; 729; 784; 841; 900; 961; 1024; 1089;
 1156; 1225; 1296; 1369; 1444; 1521; 1600; 1681; 1764; 1849; 1936; 2025; 2116;
 2209; 2304; 2401; 2500; 2601; 2704; 2809; 2916; 3025; 3136; 3249; 3364; 3481;
 3600; 3721; 3844; 3969; 4096; 4225; 4356; 4489; 4624; 4761; 4900; 5041; 5184;
 5329; 5476; 5625; 5776; 5929; 6084; 6241; 6400; 6561; 6724; 6889; 7056; 7225;
 7396; 7569; 7744; 7921; 8100; 8281; 8464; 8649; 8836; 9025; 9216; 9409; 9604;
 9801; 10000]
---------------------------------
[15; 30; 45; 60; 75; 90]
---------------------------------
[(1, 1); (1, 2); (1, 3); (1, 4); (1, 5); (1, 6); (1, 7); (1, 8); (1, 9); (1, 10);
 (2, 1); (2, 2); (2, 3); (2, 4); (2, 5); (2, 6); (2, 7); (2, 8); (2, 9); (2, 10);
 (3, 1); (3, 2); (3, 3); (3, 4); (3, 5); (3, 6); (3, 7); (3, 8); (3, 9); (3, 10)]

在本文中,我们处理了 F# 中的数组。

作者

我叫 Jan Bodnar,我是一名热情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。