切片

本文介绍如何从现有的 F# 类型中获取切片,以及如何自定义切片。

在 F# 中,切片是任何数据类型的子集。 切片与索引器相似,但它不是从基础数据结构生成单个值,而是生成多个值。 切片使用 .. 运算符语法来选择数据类型中指定索引的范围。 有关详细信息,请参阅循环表达式参考文章

F# 目前提供对切片字符串、列表、数组和多维(2D、3D、4D)数组的内部支持。 切片最常用于 F# 数组和列表。 通过在类型定义中或在范围内的类型扩展中使用 GetSlice 方法,可以向自定义数据类型添加切片。

对 F# 列表和数组进行切片

最常见的进行切片的数据类型是 F# 列表和数组。 下面的示例演示如何对列表进行切片:

// Generate a list of 100 integers
let fullList = [ 1 .. 100 ]

// Create a slice from indices 1-5 (inclusive)
let smallSlice = fullList[1..5]
printfn $"Small slice: {smallSlice}"

// Create a slice from the beginning to index 5 (inclusive)
let unboundedBeginning = fullList[..5]
printfn $"Unbounded beginning slice: {unboundedBeginning}"

// Create a slice from an index to the end of the list
let unboundedEnd = fullList[94..]
printfn $"Unbounded end slice: {unboundedEnd}"

对数组进行切片与对列表进行切片的过程类似:

// Generate an array of 100 integers
let fullArray = [| 1 .. 100 |]

// Create a slice from indices 1-5 (inclusive)
let smallSlice = fullArray[1..5]
printfn $"Small slice: {smallSlice}"

// Create a slice from the beginning to index 5 (inclusive)
let unboundedBeginning = fullArray[..5]
printfn $"Unbounded beginning slice: {unboundedBeginning}"

// Create a slice from an index to the end of the list
let unboundedEnd = fullArray[94..]
printfn $"Unbounded end slice: {unboundedEnd}"

在 F# 6 之前,切片使用带附加 . 的语法 expr.[start..finish]。 如果你愿意,仍可使用此语法。 有关详细信息,请参阅 RFC FS-1110

对多维数组进行切片

F# 支持 F# 核心库中的多维数组。 与一维数组一样,多维数组的切片也很有用。 但随着其他维度的引入,需要使用略微不同的语法才能获取特定行和列的切片。

下面的示例演示如何对 2D 数组进行切片:

// Generate a 3x3 2D matrix
let A = array2D [[1;2;3];[4;5;6];[7;8;9]]
printfn $"Full matrix:\n {A}"

// Take the first row
let row0 = A[0,*]
printfn $"{row0}"

// Take the first column
let col0 = A[*,0]
printfn $"{col0}"

// Take all rows but only two columns
let subA = A[*,0..1]
printfn $"{subA}"

// Take two rows and all columns
let subA' = A[0..1,*]
printfn $"{subA}"

// Slice a 2x2 matrix out of the full 3x3 matrix
let twoByTwo = A[0..1,0..1]
printfn $"{twoByTwo}"

为其他数据结构定义切片

F# 核心库为一组有限的类型定义了切片。 如果要为更多数据类型定义切片,可在类型定义本身或类型扩展中执行此操作。

例如,下面介绍了如何为 ArraySegment<T> 类定义切片,以便于操作数据:

open System

type ArraySegment<'TItem> with
    member segment.GetSlice(start, finish) =
        let start = defaultArg start 0
        let finish = defaultArg finish segment.Count
        ArraySegment(segment.Array, segment.Offset + start, finish - start)

let arr = ArraySegment [| 1 .. 10 |]
let slice = arr[2..5] //[ 3; 4; 5]

使用 Span<T>ReadOnlySpan<T> 类型的另一个示例:

open System

type ReadOnlySpan<'T> with
    member sp.GetSlice(startIdx, endIdx) =
        let s = defaultArg startIdx 0
        let e = defaultArg endIdx sp.Length
        sp.Slice(s, e - s)

type Span<'T> with
    member sp.GetSlice(startIdx, endIdx) =
        let s = defaultArg startIdx 0
        let e = defaultArg endIdx sp.Length
        sp.Slice(s, e - s)

let printSpan (sp: Span<int>) =
    let arr = sp.ToArray()
    printfn $"{arr}"

let sp = [| 1; 2; 3; 4; 5 |].AsSpan()
printSpan sp[0..] // [|1; 2; 3; 4; 5|]
printSpan sp[..5] // [|1; 2; 3; 4; 5|]
printSpan sp[0..3] // [|1; 2; 3|]
printSpan sp[1..3] // |2; 3|]

内置的 F# 切片包含末端

F# 中的所有内部切片都含包含末端;也就是说,切片中包含上限。 对于具有起始索引 x 和结束索引 y 的给定切片,生成的切片将包含第 y 个值。

// Define a new list
let xs = [1 .. 10]

printfn $"{xs[2..5]}" // Includes the 5th index

内置的 F# 空切片

如果语法可能生成不存在的切片,则 F# 列表、数组、序列、字符串、多维(2D、3D、4D)数组将生成一个空切片。

请考虑以下示例:

let l = [ 1..10 ]
let a = [| 1..10 |]
let s = "hello!"

let emptyList = l[-2..(-1)]
let emptyArray = a[-2..(-1)]
let emptyString = s[-2..(-1)]

重要

C# 开发人员可能希望它们引发异常,而不是生成空切片。 该设计决策是基于空集合以 F# 编写这一事实。 空 F# 列表可能由另一个 F# 列表构成,空字符串可能会添加到现有字符串,等等。 基于以参数形式传入的值获取切片很常见,此外通过生成空集合来允许超出范围 > 符合 F# 代码的组合特性。

3D 和 4D 数组的固定索引切片

对于 F# 3D 和 4D 数组,可以“固定”特定索引,并对已固定索引的其他维度进行分片。

为了说明这一点,请参考以下 3D 数组:

z = 0

x\y 0 1
0 0 1
1 2 3

z = 1

x\y 0 1
0 4 5
1 6 7

如果要从数组中提取切片 [| 4; 5 |],请使用固定索引切片。

let dim = 2
let m = Array3D.zeroCreate<int> dim dim dim

let mutable count = 0

for z in 0..dim-1 do
    for y in 0..dim-1 do
        for x in 0..dim-1 do
            m[x,y,z] <- count
            count <- count + 1

// Now let's get the [4;5] slice!
m[*, 0, 1]

最后一行固定 3D 数组的 yz 索引,并获取对应于矩阵的其余 x 值。

另请参阅