ZetCode

F# Map

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

本文探讨了如何在 F# 中有效地使用 Map 集合。

在 F# 中,map 是一个不可变的键/值对集合,旨在实现高效的查找和数据关联。与列表或数组不同,Map 通过唯一的键提供对值的快速访问,使其成为结构化数据存储的理想选择。由于 Map 是不可变的,任何修改都会创建一个新的 Map 实例,而不是改变现有的实例,从而确保了数据的一致性和函数式纯粹性。

Map 会按排序顺序维护其键,从而提供可预测的迭代行为和优化的检索性能。它们通常用于关联数据结构,例如配置文件、查找表或需要快速基于键访问的结构化集合。

F# Map 简单示例

以下是一个简单的 Map 示例。

main.fsx
let words = Map [1, "book"; 2, "sky"; 3, "work"; 4, "cloud"]

printfn "%A" words
printfn "%s" words[1]
printfn "%s" words[4]

我们有一个单词 Map。键是整数,值是字符串。

let words = Map [1, "book"; 2, "sky"; 3, "work"; 4, "cloud"]

键与值之间用逗号分隔。键值对之间用分号分隔。

printfn "%A" words

使用 %A 格式说明符,我们会美观地打印 Map。

printfn "%s" words[1]
printfn "%s" words[4]

我们打印索引为 1 和 4 的元素。

λ dotnet fsi main.fsx 
map [(1, "book"); (2, "sky"); (3, "work"); (4, "cloud")]
book
cloud

F# Map 大小

Count 属性返回 Map 中键值对的数量。

main.fsx
let words = Map [1, "book"; 2, "sky"; 3, "work"; 4, "cloud"]

let n = words.Count
printfn $"The map has {n} elements"

该示例打印 Map 中元素的数量。

F# Map 迭代

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

main.fsx
let words = Map [1, "book"; 2, "sky"; 3, "work"; 4, "cloud"]

printfn "%A" words

words |> Map.iter (fun k v -> printfn ($"{k}: {v}"))

for key in words.Keys do
    printfn "%d" key

for value in words.Values do
    printfn "%s" value

for e in words do 
    printfn $"{e.Key}: {e.Value}"

我们提供了两种基本的迭代方式:函数式和命令式。函数式使用 Map.iter 函数,而命令式使用 for 循环。

words |> Map.iter (fun k v -> printfn ($"{k}: {v}"))

Map.iter 是遍历 Map 元素的函数式方式。

for key in words.Keys do
    printfn "%d" key

我们遍历 Map 的键。

for value in words.Values do
    printfn "%s" value

我们遍历 Map 的值。

for e in words do 
    printfn $"{e.Key}: {e.Value}"

我们遍历 Map 的键值对。

λ dotnet fsi main.fsx 
map [(1, "book"); (2, "sky"); (3, "work"); (4, "cloud")]
1: book
2: sky
3: work
4: cloud
1
2
3
4
book
sky
work
cloud
1: book
2: sky
3: work
4: cloud

F# Map.filter

我们可以使用 Map.filter 过滤 Map 元素。

main.fsx
let words =
    Map [ 1, "book"
          2, "sky"
          3, "work"
          4, "cloud"
          5, "water"
          6, "war" ]

words
|> Map.filter (fun _ v -> v.Contains "w")
|> Map.values
|> Seq.iter (printfn "%s")

在程序中,我们找出所有以 'w' 开头的值。

words
|> Map.filter (fun _ v -> v.Contains "w")
|> Map.values
|> Seq.iter (printfn "%s")

我们将一个谓词 lambda 传递给 filter 方法;它检查值是否包含 'w'。结果被传递给 Map.values 来提取所有值。然后,这些值被迭代并打印到控制台。

λ dotnet fsi main.fsx 
work
water
war

F# Map Remove

Remove 方法返回一个已移除指定键值对的新 Map。

main.fsx
let words =
    Map [ 1, "book"
          2, "sky"
          3, "work"
          4, "cloud"
          5, "water"
          6, "war" ]

let res = words.Remove 1
printfn "%A" res
printfn "%A" words

在示例中,我们删除了键为 1 的元素。原始 Map 未被更改。

λ dotnet fsi main.fsx 
map [(2, "sky"); (3, "work"); ... (6, "war")]
map [(1, "book"); (2, "sky"); (3, "work"); ... (6, "war")]

F# Map Add

使用 Add,我们向 Map 添加一个新元素。

main.fsx
let words =
    Map [ 1, "book"
          2, "sky"
          3, "work"
          4, "cloud"
          5, "water"
          6, "war" ]

let res = Map.add 7 "falcon" words
printfn "%A" res
printfn "%A" words

我们向 Map 添加了一个新的键值对。

λ dotnet fsi main.fsx 
map [(1, "book"); (2, "sky"); ... (6, "war"); (7, "falcon")]
map [(1, "book"); (2, "sky"); ... (6, "war")]

F# Map.empty

我们可以使用 Map.empty 创建一个空的 Map,然后使用 Add 添加新元素。

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

let users =
   Map.empty.
      Add(1, {Name="John Doe"; Occupation="gardener"}).
      Add(2, {Name="Roger Roe"; Occupation="driver"}).
      Add(3, {Name="Lucy Smith"; Occupation="teacher"}).
      Add(4, {Name="Tom Jones"; Occupation="programmer"})

users |> Map.iter (fun k v -> printfn $"{k}: {v}")

利用 Map.empty,我们创建了一个用户 Map。

λ dotnet fsi main.fsx
1: { Name = "John Doe"
  Occupation = "gardener" }
2: { Name = "Roger Roe"
  Occupation = "driver" }
3: { Name = "Lucy Smith"
  Occupation = "teacher" }
4: { Name = "Tom Jones"
  Occupation = "programmer" }

F# Map 列表

在下一个示例中,我们定义了一个 Map 列表。

main.fsx
let fruits1 = Map [ "oranges", 2; "bananas", 3 ]
let fruits2 = Map [ "plums", 4; "kiwis", 5 ]

let all = [ Map[1, fruits1]; Map[2, fruits2] ]

all
|> List.iter (Map.iter (fun k v -> printfn $"{k} {v}"))

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

for nested in all do
  for e in nested do
        printfn $"{e.Key} {e.Value}"

我们定义了两个 Map,并将它们插入到一个列表中。然后以声明式和命令式方式迭代该列表。

all
|> List.iter (Map.iter (fun k v -> printfn $"{k} {v}"))

我们使用 List.iterMap.iter 以声明式方式迭代 Map 列表。

for nested in all do
    for e in nested do
          printfn $"{e.Key} {e.Value}"

命令式地,我们使用两个 for 循环遍历列表。

λ dotnet fsi main.fsx
1 map [(bananas, 3); (oranges, 2)]
2 map [(kiwis, 5); (plums, 4)]
-------------------
1 map [(bananas, 3); (oranges, 2)]
2 map [(kiwis, 5); (plums, 4)]

在本文中,我们学习了 F# 中的 Map。

作者

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