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


Срезы

В этой статье объясняется, как принимать срезы из существующих типов 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результирующий срез будет содержать значение yth .

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

Срезы фиксированного индекса для трехмерных и 4D-массивов

Для массивов F# 3D и 4D можно "исправить" определенный индекс и срезать другие измерения с фиксированным индексом.

Чтобы проиллюстрировать это, рассмотрим следующий трехмерный массив:

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 и z индексы трехмерного массива и принимает остальные x значения, соответствующие матрице.

См. также