ZetCode

F# 集合

最后修改日期:2025 年 5 月 17 日

本文将探讨 F# 中的集合。集合是函数式编程中的一种基本数据结构,提供了一种存储唯一元素和执行集合运算的方法。

F# 中的集合是不可变的集合,不包含重复元素。它们提供高效的查找操作,并支持常见的集合操作,如并集、交集和差集。F# 集合实现为不可变平衡二叉树,这使其在函数式编程模式下非常高效。

由于 F# 中的集合是不可变的,任何修改集合的操作(如添加或删除元素)都会返回一个新的集合,而不是更改原始集合。这确保了数据的完整性,并允许安全的并发编程。此外,底层的平衡树结构确保了集合操作(如成员资格测试和迭代)即使在处理大型数据集时也能保持高性能。

创建集合

有几种方法可以在 F# 中创建集合。你可以从序列、列表、数组创建它们,或者使用集合推导式。Set 模块提供了用于处理集合的函数。

creating_sets.fsx
// Creating empty set
let emptySet = Set.empty
printfn "Empty set: %A" emptySet

// Using set literal
let uniqeueVals = set [ 1; 2; 3; 4; 5 ]
printfn "Unique values: %A" uniqeueVals

// Creating set from list
let numbers = Set.ofList [ 1; 2; 3; 2; 1 ]
printfn "From list: %A" numbers

// Creating set from array
let colors =
    Set.ofArray [| "red"
                   "green"
                   "blue"
                   "green" |]

printfn "From array: %A" colors

// Helper function to check if a number is prime
let isPrime n =
    if n < 2 then
        false
    else
        seq { 2 .. int (sqrt (float n)) }
        |> Seq.forall (fun x -> n % x <> 0)

// Creating set from sequence
let primes =
    Set.ofSeq (
        seq {
            for i in 1..10 do
                if isPrime i then yield i
        }
    )

printfn "Primes: %A" primes

// Set comprehension
let squares = Set.ofSeq (seq { for x in 1..5 -> x * x })
printfn "Squares: %A" squares

此示例演示了创建集合的不同方法。请注意重复元素是如何自动删除的。Set.empty 函数创建一个空集合。set 函数从列表创建集合,而 ofSeq 函数允许你从任何序列(包括数组和列表)创建集合。isPrime 函数是检查数字是否为素数的辅助函数。

λ dotnet fsi creating_sets.fsx
Empty set: set []
Unique values: set [1; 2; 3; 4; 5]
From list: set [1; 2; 3]
From array: set ["blue"; "green"; "red"]
Primes: set [2; 3; 5; 7]
Squares: set [1; 4; 9; 16; 25]

基本集合操作

F# 集合支持基本操作,如添加元素、检查成员资格和计算元素数量。由于集合是不可变的,修改集合的操作会返回一个新的集合,而不是更改原始集合。

basic_operations.fsx
let mySet = set [1; 2; 3; 4; 5]

// Adding elements
let biggerSet = mySet.Add(6).Add(7)
printfn "After adding: %A" biggerSet

// Removing elements
let smallerSet = mySet.Remove(5).Remove(1)
printfn "After removing: %A" smallerSet

// Checking membership
printfn "Contains 3? %b" (mySet.Contains 3)
printfn "Contains 8? %b" (mySet.Contains 8)

// Counting elements
printfn "Count: %d" mySet.Count

// Minimum and maximum
printfn "Min: %d" (Set.minElement mySet)
printfn "Max: %d" (Set.maxElement mySet)

此代码展示了基本集合操作。AddRemove 返回新的集合。Contains 检查成员资格,Count 返回元素数量。minElementmaxElement 返回最小和最大的元素。

λ dotnet fsi basic_operations.fsx
After adding: set [1; 2; 3; 4; 5; 6; 7]
After removing: set [2; 3; 4]
Contains 3? true
Contains 8? false
Count: 5
Min: 1
Max: 5

集合操作:并集、交集、差集

F# 集合支持数学集合操作,包括并集、交集和差集。这些操作使用 Set 模块的函数或等效运算符来执行。

set_operations.fsx
let setA = set [1; 2; 3; 4; 5]
let setB = set [4; 5; 6; 7; 8]

// Union (elements in either set)
let unionAB = Set.union setA setB
printfn "Union: %A" unionAB

// Alternative union syntax
let unionAB2 = setA + setB
printfn "Union with +: %A" unionAB2

// Intersection (elements in both sets)
let intersectAB = Set.intersect setA setB
printfn "Intersection: %A" intersectAB

// Difference (elements in first set but not second)
let diffAB = Set.difference setA setB
printfn "Difference A-B: %A" diffAB

// Symmetric difference (elements in either set but not both)
let symDiffAB = (setA - setB) + (setB - setA)
printfn "Symmetric difference: %A" symDiffAB

此示例演示了基本集合操作。+ 运算符可用于并集,而 - 用于差集。请注意,对称差集不是内置的,但可以从其他操作构建。

λ dotnet fsi set_operations.fsx
Union: set [1; 2; 3; 4; 5; 6; 7; 8]
Union with +: set [1; 2; 3; 4; 5; 6; 7; 8]
Intersection: set [4; 5]
Difference A-B: set [1; 2; 3]
Symmetric difference: set [1; 2; 3; 6; 7; 8]

集合比较和子集操作

集合可以进行相等性和子集关系的比较。F# 提供了函数来检查一个集合是否是另一个集合的子集或超集,或者它们是否包含完全相同的元素。

set_comparisons.fsx
let setX = set [1; 2; 3; 4]
let setY = set [3; 4; 5; 6]
let setZ = set [3; 4]

// Equality
printfn "X equals Y? %b" (setX = setY)
printfn "X equals X? %b" (setX = setX)

// Subset checks
printfn "Z subset of X? %b" (Set.isSubset setZ setX)
printfn "Z proper subset of X? %b" (Set.isProperSubset setZ setX)
printfn "Z subset of Y? %b" (Set.isSubset setZ setY)

// Superset checks
printfn "X superset of Z? %b" (Set.isSuperset setX setZ)
printfn "Y superset of Z? %b" (Set.isSuperset setY setZ)

该示例展示了如何比较集合。isSubset 检查一个集合的所有元素是否在另一个集合中,而 isProperSubset 要求集合不同。isSupersetisSubset 的逆运算。

λ dotnet fsi set_comparisons.fsx
X equals Y? false
X equals X? true
Z subset of X? true
Z proper subset of X? true
Z subset of Y? true
X superset of Z? true
Y superset of Z? true

集合转换和过滤

可以使用 mapfilter 等高阶函数对集合进行转换和过滤。这些操作会返回包含转换或过滤后元素的新集合。

set_transformations.fsx
let numbers = set [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]

// Mapping a function over a set
let squares = Set.map (fun x -> x * x) numbers
printfn "Squares: %A" squares

// Filtering elements
let evens = Set.filter (fun x -> x % 2 = 0) numbers
printfn "Evens: %A" evens

// Folding (reducing) a set
let sum = Set.fold (fun acc x -> acc + x) 0 numbers
printfn "Sum: %d" sum

// Iterating over a set
printf "Elements: "
Set.iter (fun x -> printf "%d " x) numbers
printfn ""

此代码演示了集合上的函数式转换。map 将函数应用于每个元素,filter 选择匹配谓词的元素,fold 将集合简化为单个值,iter 对每个元素执行操作。

λ dotnet fsi set_transformations.fsx
Squares: set [1; 4; 9; 16; 25; 36; 49; 64; 81; 100]
Evens: set [2; 4; 6; 8; 10]
Sum: 55
Elements: 1 2 3 4 5 6 7 8 9 10 

使用自定义类型

集合可以包含自定义类型,但这些类型必须支持比较。对于记录和区分联合,如果所有组成类型都支持比较,F# 会自动生成比较实现。

custom_types.fsx
type Person = {
    Name: string
    Age: int
}

let people = set [
    { Name = "Alice"; Age = 30 }
    { Name = "Bob"; Age = 25 }
    { Name = "Alice"; Age = 30 } // Duplicate
    { Name = "Charlie"; Age = 35 }
]

printfn "People set: %A" people
printfn "Count: %d" people.Count

type Shape =
    | Circle of radius: float
    | Rectangle of width: float * height: float

let shapes = set [
    Circle 2.5
    Rectangle (3.0, 4.0)
    Circle 2.5 // Duplicate
    Rectangle (1.0, 2.0)
]

printfn "Shapes set: %A" shapes
printfn "Contains Circle 2.5? %b" (shapes.Contains (Circle 2.5))

此示例展示了包含自定义类型的集合。Person 记录和 Shape 区分联合自动支持比较,因此它们可以在集合中使用。重复项会根据比较自动删除。

λ dotnet fsi custom_types.fsx
People set: set [{ Name = "Alice"
 Age = 30; }; { Name = "Bob"
 Age = 25; }; { Name = "Charlie"
 Age = 35; }]
Count: 3
Shapes set: set [Circle 2.5; Rectangle (1.0, 2.0); Rectangle (3.0, 4.0)]
Contains Circle 2.5? true

F# 集合是强大的不可变集合,提供高效的查找操作并支持数学集合操作。其不可变性使其成为函数式编程模式的理想选择,而其作为平衡二叉树的实现则确保了良好的性能。无论你需要处理唯一的数据集合还是执行集合论运算,F# 集合都提供了稳健的解决方案。

作者

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