元组

元组是一组未命名但有序的值,值的类型可能不同。 元组可以是引用类型或结构。

语法

(element, ... , element)
struct(element, ... ,element )

备注

上述语法中的每个 element 都可以是任何有效的 F# 表达式。

示例

元组的示例包括相同或不同类型的对、三元组等。 以下代码演示了一些示例。

(1, 2)

// Triple of strings.
("one", "two", "three")

// Tuple of generic types.
(a, b)

// Tuple that has mixed types.
("one", 1, 2.0)

// Tuple of integer expressions.
(a + 1, b + 1)

// Struct Tuple of floats
struct (1.025f, 1.5f)

获取单个值

可以使用模式匹配来访问和分配元组元素的名称,如以下代码所示。

let print tuple1 =
   match tuple1 with
    | (a, b) -> printfn "Pair %A %A" a b

还可以使用 let 绑定在 match 表达式外部通过模式匹配来析构元组:

let (a, b) = (1, 2)

// Or as a struct
let struct (c, d) = struct (1, 2)

或者,可以对作为函数输入的元组进行模式匹配:

let getDistance ((x1,y1): float*float) ((x2,y2): float*float) =
    // Note the ability to work on individual elements
    (x1*x2 - y1*y2) 
    |> abs 
    |> sqrt

如果只需要元组的一个元素,可以使用通配符(下划线)避免为不需要的值创建新名称。

let (a, _) = (1, 2)

将引用元组中的元素复制到结构元组也很简单:

// Create a reference tuple
let (a, b) = (1, 2)

// Construct a struct tuple from it
let struct (c, d) = struct (a, b)

函数 fstsnd(仅限引用元组)分别返回元组的第一个和第二个元素。

let c = fst (1, 2)
let d = snd (1, 2)

没有返回三元组第三个元素的内置函数,但你可以按下面所示轻松编写一个。

let third (_, _, c) = c

通常,最好使用模式匹配来访问单个元组元素。

使用元组

元组提供一种从函数返回多个值的便捷方式,如以下示例所示。 此示例执行整数除法运算,并将运算的舍入结果作为元组对的第一个成员返回,余数作为该对的第二个成员返回。

let divRem a b =
   let x = a / b
   let y = a % b
   (x, y)

如果要避免隐式扩充常用函数语法所隐含的函数参数,元组也可以用作函数参数。

let sumNoCurry (a, b) = a + b

借助定义函数 let sum a b = a + b 的常用语法,可以定义一个函数,该函数是函数第一个参数的部分应用,如以下代码所示。

let sum a b = a + b

let addTen = sum 10
let result = addTen 95
// Result is 105.

使用元组作为参数会禁用扩充。 有关详细信息,请参阅函数中的“参数的部分应用”。

元组类型的名称

写出作为元组的类型的名称时,使用 * 符号来分隔元素。 对于由 intfloatstring 组成的元组,例如 (10, 10.0, "ten"),类型将编写如下。

int * float * string

请注意,在为结构元组类型创建类型别名时,必须使用外括号。

type TupleAlias = string * float
type StructTupleAlias = (struct (string * float))

与 C# 元组的互操作

C# 中的元组是结构,相当于 F# 中的结构元组。 如果需要与 C# 互操作,则必须使用结构元组。

这很容易做到。 例如,假设你必须将元组传递给 C# 类,然后使用它的结果(也是一个元组):

namespace CSharpTupleInterop
{
    public static class Example
    {
        public static (int, int) AddOneToXAndY((int x, int y) a) =>
            (a.x + 1, a.y + 1);
    }
}

然后,在 F# 代码中,你可以将结构元组作为参数传递,并将结果用作结构元组。

open TupleInterop

let struct (newX, newY) = Example.AddOneToXAndY(struct (1, 2))
// newX is now 2, and newY is now 3

引用元组和结构元组之间的转换

引用元组和结构元组具有完全不同的基础表示形式,因此它们不能隐式转换。 也就是说,以下代码将无法编译:

// Will not compile!
let (a, b) = struct (1, 2)

// Will not compile!
let struct (c, d) = (1, 2)

// Won't compile!
let f(t: struct(int*int)): int*int = t

你必须对一个元组进行模式匹配,并用构成部分构造另一个元组。 例如:

// Pattern match on the result.
let (a, b) = (1, 2)

// Construct a new tuple from the parts you pattern matched on.
let struct (c, d) = struct (a, b)

引用元组的编译形式

本部分说明编译时元组的形式。 除非你的目标是 .NET Framework 3.5 或更低版本,否则无需阅读此处的信息。

元组被编译成某种泛型类型的对象,这些对象(全部命名为 System.Tuple)将在 arity 或类型参数数量上重载。 当你通过另一种语言(例如 C# 或 Visual Basic)查看元组类型时,或者当你使用不了解 F# 构造的工具时,元组类型就会以这种形式出现。 Tuple 类型是在 .NET Framework 4 中引入的。 如果你的目标是 .NET Framework 的早期版本,编译器会使用 F# 核心库 2.0 版本中的 System.Tuple 版本。 此库中的类型仅用于面向 .NET Framework 2.0、3.0 和 3.5 版本的应用程序。 类型转发用于确保 .NET Framework 2.0 和 .NET Framework 4 F# 组件之间的二进制兼容性。

结构元组的编译形式

结构元组(例如 struct (x, y))在本质上不同于引用元组。 它们被编译成 ValueTuple 类型,由 arity 或类型参数数量重载。 它们等效于 C# 元组Visual Basic 元组,并且可以双向互操作。

另请参阅