Редагувати

Поділитися через


Tuples

A tuple is a grouping of unnamed but ordered values, possibly of different types. Tuples can either be reference types or structs.

Syntax

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

Remarks

Each element in the previous syntax can be any valid F# expression.

Examples

Examples of tuples include pairs, triples, and so on, of the same or different types. Some examples are illustrated in the following code.

(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)

Obtaining Individual Values

You can use pattern matching to access and assign names for tuple elements, as shown in the following code.

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

You can also deconstruct a tuple via pattern matching outside of a match expression via let binding:

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

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

Or you can pattern match on tuples as inputs to functions:

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

If you need only one element of the tuple, the wildcard character (the underscore) can be used to avoid creating a new name for a value that you do not need.

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

Copying elements from a reference tuple into a struct tuple is also simple:

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

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

The functions fst and snd (reference tuples only) return the first and second elements of a tuple, respectively.

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

There is no built-in function that returns the third element of a triple, but you can easily write one as follows.

let third (_, _, c) = c

Generally, it is better to use pattern matching to access individual tuple elements.

Using Tuples

Tuples provide a convenient way to return multiple values from a function, as shown in the following example. This example performs integer division and returns the rounded result of the operation as a first member of a tuple pair and the remainder as a second member of the pair.

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

Tuples can also be used as function arguments when you want to avoid the implicit currying of function arguments that is implied by the usual function syntax.

let sumNoCurry (a, b) = a + b

The usual syntax for defining the function let sum a b = a + b enables you to define a function that is the partial application of the first argument of the function, as shown in the following code.

let sum a b = a + b

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

Using a tuple as the parameter disables currying. For more information, see "Partial Application of Arguments" in Functions.

Names of Tuple Types

When you write out the name of a type that is a tuple, you use the * symbol to separate elements. For a tuple that consists of an int, a float, and a string, such as (10, 10.0, "ten"), the type would be written as follows.

int * float * string

Note that outer parentheses are mandatory when creating a type alias for a struct tuple type.

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

Interoperation with C# Tuples

Tuples in C# are structs, and are equivalent to struct tuples in F#. If you need to interoperate with C#, you must use struct tuples.

This is easy to do. For example, imagine you have to pass a tuple to a C# class and then consume its result, which is also a tuple:

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

In your F# code, you can then pass a struct tuple as the parameter and consume the result as a struct tuple.

open TupleInterop

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

Converting between Reference Tuples and Struct Tuples

Because Reference Tuples and Struct Tuples have a completely different underlying representation, they are not implicitly convertible. That is, code such as the following won't compile:

// 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

You must pattern match on one tuple and construct the other with the constituent parts. For example:

// 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)

Compiled Form of Reference Tuples

This section explains the form of tuples when they're compiled. The information here isn't necessary to read unless you are targeting .NET Framework 3.5 or lower.

Tuples are compiled into objects of one of several generic types, all named System.Tuple, that are overloaded on the arity, or number of type parameters. Tuple types appear in this form when you view them from another language, such as C# or Visual Basic, or when you are using a tool that is not aware of F# constructs. The Tuple types were introduced in .NET Framework 4. If you are targeting an earlier version of .NET Framework, the compiler uses versions of System.Tuple from the 2.0 version of the F# Core Library. The types in this library are used only for applications that target the 2.0, 3.0, and 3.5 versions of .NET Framework. Type forwarding is used to ensure binary compatibility between .NET Framework 2.0 and .NET Framework 4 F# components.

Compiled Form of Struct Tuples

Struct tuples (for example, struct (x, y)), are fundamentally different from reference tuples. They are compiled into the ValueTuple type, overloaded by arity, or the number of type parameters. They are equivalent to C# Tuples and Visual Basic Tuples, and interoperate bidirectionally.

See also