Řezy
Tento článek vysvětluje, jak převést řezy z existujících typů jazyka F# a jak definovat vlastní řezy.
V jazyce F# je řez podmnožinou libovolného datového typu. Řezy se podobají indexerům, ale místo toho, aby z podkladové datové struktury přinesly jednu hodnotu, poskytují více hodnot. Řezy používají syntaxi operátoru ..
k výběru rozsahu zadaných indexů v datovém typu. Další informace najdete v článku s referenčními informacemi o výrazech smyčky.
Jazyk F# v současné době podporuje vytváření řezů řetězců, seznamů, polí a multidimenzionálních polí (2D, 3D, 4D). Řezy se nejčastěji používají s poli a seznamy jazyka F#. Řezy můžete přidat do vlastních datových typů pomocí GetSlice
metody v definici typu nebo v rozšíření typu v oboru.
Vytváření řezů seznamů a polí jazyka F#
Nejběžnějšími datovými typy, které jsou průřezy, jsou seznamy a pole jazyka F#. Následující příklad ukazuje, jak rozdělit seznamy:
// 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}"
Vytváření řezů polí je stejně jako seznamy řezů:
// 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}"
Před F# 6 použil řez syntaxi expr.[start..finish]
s extra .
. Pokud zvolíte tuto syntaxi, můžete tuto syntaxi přesto použít. Další informace najdete v dokumentu RFC FS-1110.
Vytváření řezů multidimenzionálních polí
Jazyk F# podporuje multidimenzionální pole v základní knihovně jazyka F#. Stejně jako u jednorozměrných polí můžou být užitečné i řezy multidimenzionálních polí. Zavedení dalších dimenzí však vyžaduje trochu odlišnou syntaxi, takže můžete vzít řezy konkrétních řádků a sloupců.
Následující příklady ukazují, jak rozdělit 2D pole:
// 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}"
Definování řezů pro jiné datové struktury
Základní knihovna jazyka F# definuje řezy pro omezenou sadu typů. Pokud chcete definovat řezy pro více datových typů, můžete to udělat buď v samotné definici typu, nebo v rozšíření typu.
Tady je příklad, jak můžete definovat řezy pro ArraySegment<T> třídu, aby umožňovala pohodlnou manipulaci s daty:
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]
Další příklad použití a Span<T>ReadOnlySpan<T> typů:
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|]
Předdefinované řezy jazyka F# jsou komplexní
Všechny vnitřní řezy v jazyce F# jsou komplexní; to znamená, že horní mez je součástí řezu. Pro daný řez s počátečním indexem x
a koncovým indexem y
bude výsledný řez obsahovat hodnotu yth .
// Define a new list
let xs = [1 .. 10]
printfn $"{xs[2..5]}" // Includes the 5th index
Předdefinované prázdné řezy jazyka F#
Seznamy, matice, sekvence, řetězce, multidimenzionální pole (2D, 3D, 4D) všechna vytvoří prázdný řez, pokud by syntaxe mohla vytvořit řez, který neexistuje.
Představte si následující příklad:
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)]
Důležité
Vývojáři jazyka C# můžou očekávat, že místo vytvoření prázdného řezu vyvolá výjimku. Toto je rozhodnutí o návrhu založené na faktu, že prázdné kolekce se skládají v jazyce F#. Prázdný seznam jazyka F# lze sestavit s jiným seznamem jazyka F#, prázdný řetězec lze přidat do existujícího řetězce atd. Může být běžné vzít řezy na základě hodnot předaných jako parametry a být odolné vůči mimohraničí > tím, že vytvoří prázdnou kolekci, která odpovídá kompoziční povaze kódu F#.
Řezy s pevným indexem pro 3D a 4D pole
U polí F# 3D a 4D můžete "opravit" určitý index a rozdělit další dimenze s pevným indexem.
Pro ilustraci zvažte následující 3D pole:
z = 0
x\y | 0 | 1 |
---|---|---|
0 | 0 | 1 |
1 | 2 | 3 |
z = 1
x\y | 0 | 1 |
---|---|---|
0 | 4 | 5 |
1 | 6 | 7 |
Pokud chcete extrahovat řez [| 4; 5 |]
z pole, použijte řez s pevným indexem.
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]
Poslední řádek opraví y
a z
indexy 3D pole a vezme zbývající hodnoty x
, které odpovídají matici.