本文介绍如何从现有 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]
最后一行修复 y
了 3D 数组的索引, z
并获取对应于矩阵的 x
其余值。