F# 元组
最后修改日期:2025 年 5 月 17 日
F# 中的元组是轻量级数据结构,可将多个值(可能具有不同类型)组合在一起。它们是不可变的、有序的集合,提供了一种从函数返回多个值的便捷方式,或者在不定义自定义类型的情况下临时组合相关值。
创建元组
F# 中的元组是通过将逗号分隔的值括在括号中来创建的。虽然可以定义任何大小的元组,但它们最常用于数量适中的元素,以保持可读性和效率。F# 为包含最多七个元素的元组提供了内置优化,通常称为“元组对”到“元组七重奏”,但仍可以创建和使用更大的元组,没有任何限制。
// Pair (2-tuple) let nameAge = ("John", 32) printfn "Name and age: %A" nameAge // Triple (3-tuple) let rgbColor = (255, 128, 64) printfn "RGB color: %A" rgbColor // Mixed types let personData = ("Alice", 28, 165.5, true) printfn "Person data: %A" personData // Single-element tuples do not exist in F# // let single = ("hello",) // Empty tuple (unit) let empty = () printfn "Empty tuple: %A" empty
此示例展示了创建元组的不同方法。请注意,F# 中不存在单元素元组,而空元组 ()
代表 unit
类型。
λ dotnet fsi creating_tuples.fsx Name and age: ("John", 32) RGB color: (255, 128, 64) Person data: ("Alice", 28, 165.5, true) Empty tuple: ()
省略括号
F# 允许在不使用括号的情况下定义元组,从而使语法更简洁、更精炼。但是,当语法不明确时,这可能导致混淆。例如,表达式 1, 2 + 3
被解释为元组 (1, 5)
,而不是预期的 (1, 2) + 3
。为避免歧义,建议在创建元组时使用括号,尤其是在复杂表达式中。
let nameAge = "John", 32 printfn "Name and age: %A" nameAge let rgbColor = 255, 128, 64 printfn "RGB color: %A" rgbColor let personData = "Alice", 28, 165.5, true printfn "Person data: %A" personData
此示例显示了在创建元组时可以省略括号。
访问元组元素
F# 提供了几种访问元组元素的方法。您可以使用模式匹配、用于对的 fst
和 snd
函数,或直接解构。
let person = ("Robert", "Novak", 45) // Using fst and snd (for pairs only) let pair = ("John", "Doe") printfn "First: %s, Last: %s" (fst pair) (snd pair) // Pattern matching match person with | (firstName, lastName, age) -> printfn "%s %s is %d years old" firstName lastName age // Deconstructing directly let (fName, lName, years) = person printfn "Deconstructed: %s %s, age %d" fName lName years // Accessing elements of larger tuples let coords = (10.5, 20.3, 5.0, "home") let (x, y, z, label) = coords printfn "Coordinates: %s (%.1f, %.1f, %.1f)" label x y z
此代码演示了访问元组元素的各种方法。模式匹配是最灵活的方法,而 fst
和 snd
对于对很方便。解构直接将元素分配给变量。
λ dotnet fsi accessing_elements.fsx First: John, Last: Doe Robert Novak is 45 years old Deconstructed: Robert Novak, age 45 Coordinates: home (10.5, 20.3, 5.0)
将元组与函数一起使用
元组通常用于从函数返回多个值,或以结构化的方式将多个参数传递给函数。在许多情况下,它们提供了 out 参数或自定义类型的简单替代方案。
// Function returning a tuple let divide x y = let quotient = x / y let remainder = x % y (quotient, remainder) let result = divide 15 4 printfn "15 divided by 4: %A" result // Function taking a tuple parameter let printCoordinates (x, y, z) = printfn "Position: (%.1f, %.1f, %.1f)" x y z let position = (3.5, 2.0, 1.5) printCoordinates position // Curried vs tuple arguments let addCurried x y = x + y let addTupled (x, y) = x + y printfn "Curried add: %d" (addCurried 3 4) printfn "Tupled add: %d" (addTupled (3, 4))
此示例展示了返回元组和接受元组参数的函数。请注意柯里化函数(多个参数)和接受单个元组参数的函数之间的区别。元组版本要求一次性提供所有参数。
λ dotnet fsi functions_with_tuples.fsx 15 divided by 4: (3, 3) Position: (3.5, 2.0, 1.5) Curried add: 7 Tupled add: 7
元组模式匹配
使用元组进行模式匹配可以根据元组的结构和内容进行简洁的分支逻辑。这是处理代码中不同情况的强大功能。
let classifyPoint (x, y) = match (x, y) with | (0.0, 0.0) -> "Origin" | (_, 0.0) -> "On x-axis" | (0.0, _) -> "On y-axis" | (x, y) when x > 0.0 && y > 0.0 -> "Quadrant I" | (x, y) when x < 0.0 && y > 0.0 -> "Quadrant II" | (x, y) when x < 0.0 && y < 0.0 -> "Quadrant III" | _ -> "Quadrant IV" printfn "%s" (classifyPoint (0.0, 0.0)) printfn "%s" (classifyPoint (2.5, 0.0)) printfn "%s" (classifyPoint (-1.0, 3.0)) printfn "%s" (classifyPoint (3.0, -4.0)) let describeSize (width, height) = match (width, height) with | (w, h) when w = h -> "Square" | (w, h) when w > h -> "Landscape" | _ -> "Portrait" printfn "%s" (describeSize (100, 100)) printfn "%s" (describeSize (200, 100)) printfn "%s" (describeSize (80, 120))
该示例演示了使用元组模式匹配对二维平面中的点进行分类以及描述矩形比例。下划线 _
用作通配符,匹配任何值。
λ dotnet fsi pattern_matching.fsx Origin On x-axis Quadrant II Quadrant IV Square Landscape Portrait
元组类型注解
可以显式注解元组类型以指定其元素的类型。这对于文档很有用,并且可以帮助在编译时捕获类型错误。
// Annotating tuple types let person: string * int = ("Alice", 30) let coords: float * float * string = (45.2, -122.6, "Portland") // Function with annotated tuple parameter let printPerson (p: string * int) = let (name, age) = p printfn "%s is %d years old" name age printPerson person // Type inference with tuples let getDimensions () : int * int = let width = 800 let height = 600 (width, height) let (screenWidth, screenHeight) = getDimensions() printfn "Resolution: %dx%d" screenWidth screenHeight
此代码显示了如何向元组添加类型注解。星号 *
用于分隔元组元素的类型。可以在元组变量、参数和返回类型中添加注解。
λ dotnet fsi type_annotations.fsx Alice is 30 years old Resolution: 800x600
比较元组
当所有元素都可比较时,F# 中的元组支持结构化比较。比较是按顺序逐个元素进行的。
let point1 = (2, 3) let point2 = (2, 4) let point3 = (1, 5) printfn "point1 = point2? %b" (point1 = point2) printfn "point1 < point2? %b" (point1 < point2) printfn "point1 > point3? %b" (point1 > point3) // Sorting a list of tuples let points = [(1, 5); (2, 3); (2, 4); (1, 4)] let sorted = List.sort points printfn "Sorted points: %A" sorted // Comparing tuples with different types let a = ("apple", 3) let b = ("banana", 2) printfn "'apple' vs 'banana': %b" (a < b)
该示例演示了元组比较。元组首先按第一个元素比较,如果第一个元素相等则按第二个元素比较,依此类推。这使得它们在所有元素都可比较时可以自然地排序。
λ dotnet fsi comparing_tuples.fsx point1 = point2? false point1 < point2? true point1 > point3? true Sorted points: [(1, 4); (1, 5); (2, 3); (2, 4)] 'apple' vs 'banana': true
F# 元组是多功能、轻量级的数据结构,在临时组合相关值和从函数返回多个值方面表现出色。它们简单的语法、模式匹配支持和内置比较使其成为许多编程场景的理想选择。虽然它们不应取代复杂的域建模的适当数据类型,但元组是任何 F# 程序员工具箱中的重要工具。