Pole (F#)
Pole jsou pevná, nulová, proměnlivá kolekce po sobě jdoucích datových prvků, které jsou všechny stejného typu.
Vytváření polí
Pole můžete vytvářet několika způsoby. Malé pole můžete vytvořit výpisem po sobě jdoucích hodnot a [|
|]
oddělených středníky, jak je znázorněno v následujících příkladech.
let array1 = [| 1; 2; 3 |]
Každý prvek můžete také umístit na samostatný řádek, v takovém případě je oddělovač středníku volitelný.
let array1 =
[|
1
2
3
|]
Typ prvků pole je odvozen z použitých literálů a musí být konzistentní.
// This is an array of 3 integers.
let array1 = [| 1; 2; 3 |]
// This is an array of a tuple of 3 integers.
let array2 = [| 1, 2, 3 |]
Následující kód způsobí chybu, protože 3.0 je float a 1 a 2 jsou celá čísla.
// Causes an error. The 3.0 (float) cannot be converted to integer implicitly.
// let array3 = [| 1; 2; 3.0 |]
Následující kód také způsobí chybu, protože 1,2
je řazená kolekce členů a 3
je celé číslo.
// Causes an error too. The 3 (integer) cannot be converted to tuple implicitly.
// let array4 = [| 1, 2; 3 |]
K vytváření polí můžete také použít sekvenční výrazy. Následuje příklad, který vytvoří pole čtverců celých čísel od 1 do 10.
let array3 = [| for i in 1 .. 10 -> i * i |]
Chcete-li vytvořit pole, ve kterém jsou všechny prvky inicializovány na nulu, použijte Array.zeroCreate
.
let arrayOfTenZeroes : int array = Array.zeroCreate 10
Přístupové prvky
K prvkům pole můžete přistupovat pomocí hranatých závorek ([
a ]
). Původní syntaxe tečky (.[index]
) se stále podporuje, ale už se nedoporučuje jako F# 6.0.
array1[0]
Indexy polí začínají na 0.
K prvkům pole můžete přistupovat také pomocí zápisu řezu, který umožňuje zadat podadresa pole. Příklady zápisu řezu následují.
// Accesses elements from 0 to 2.
array1[0..2]
// Accesses elements from the beginning of the array to 2.
array1[..2]
// Accesses elements from 2 to the end of the array.
array1[2..]
Při použití zápisu řezu se vytvoří nová kopie pole.
Typy polí a moduly
Typ všech polí jazyka F# je typ System.Arrayrozhraní .NET Framework . Proto pole jazyka F# podporují všechny funkce, které jsou k dispozici v System.Array.
Modul Array
podporuje operace s jednorozměrnými poli. Moduly Array2D
, Array3D
a Array4D
obsahují funkce, které podporují operace s poli dvou, tří a čtyř dimenzí, v uvedeném pořadí. Pole pořadí větší než čtyři můžete vytvořit pomocí .System.Array
Jednoduché funkce
Array.get
získá prvek. Array.length
poskytuje délku pole. Array.set
nastaví prvek na zadanou hodnotu. Následující příklad kódu znázorňuje použití těchto funkcí.
let array1 = Array.create 10 ""
for i in 0 .. array1.Length - 1 do
Array.set array1 i (i.ToString())
for i in 0 .. array1.Length - 1 do
printf "%s " (Array.get array1 i)
Výstup je následující.
0 1 2 3 4 5 6 7 8 9
Funkce, které vytvářejí pole
Několik funkcí vytváří pole bez nutnosti existujícího pole. Array.empty
vytvoří nové pole, které neobsahuje žádné prvky. Array.create
vytvoří pole zadané velikosti a nastaví všechny prvky na zadané hodnoty. Array.init
vytvoří pole s ohledem na dimenzi a funkci pro vygenerování prvků. Array.zeroCreate
vytvoří matici, ve které jsou všechny prvky inicializovány na nulovou hodnotu pro typ pole. Následující kód ukazuje tyto funkce.
let myEmptyArray = Array.empty
printfn "Length of empty array: %d" myEmptyArray.Length
printfn "Array of floats set to 5.0: %A" (Array.create 10 5.0)
printfn "Array of squares: %A" (Array.init 10 (fun index -> index * index))
let (myZeroArray : float array) = Array.zeroCreate 10
Výstup je následující.
Length of empty array: 0
Area of floats set to 5.0: [|5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0|]
Array of squares: [|0; 1; 4; 9; 16; 25; 36; 49; 64; 81|]
Array.copy
vytvoří novou matici obsahující prvky, které se zkopírují z existujícího pole. Všimněte si, že kopie je mělká kopie, což znamená, že pokud je typ prvku odkazem, zkopíruje se pouze odkaz, nikoli podkladový objekt. Následující příklad kódu to dokládá.
open System.Text
let firstArray : StringBuilder array = Array.init 3 (fun index -> new StringBuilder(""))
let secondArray = Array.copy firstArray
// Reset an element of the first array to a new value.
firstArray[0] <- new StringBuilder("Test1")
// Change an element of the first array.
firstArray[1].Insert(0, "Test2") |> ignore
printfn "%A" firstArray
printfn "%A" secondArray
Výstup předchozího kódu je následující:
[|Test1; Test2; |]
[|; Test2; |]
Řetězec Test1
se zobrazí pouze v prvním poli, protože operace vytvoření nového prvku přepíše odkaz v firstArray
, ale nemá vliv na původní odkaz na prázdný řetězec, který je stále přítomn secondArray
. Řetězec Test2
se zobrazí v obou polích, protože Insert
operace typu System.Text.StringBuilder má vliv na základní System.Text.StringBuilder objekt, na který se odkazuje v obou polích.
Array.sub
vygeneruje nové pole z poduspořádku pole. Rozsah zadáte zadáním počátečního indexu a délky. Následující kód ukazuje použití Array.sub
.
let a1 = [| 0 .. 99 |]
let a2 = Array.sub a1 5 10
printfn "%A" a2
Výstup ukazuje, že dílčí pole začíná na elementu 5 a obsahuje 10 prvků.
[|5; 6; 7; 8; 9; 10; 11; 12; 13; 14|]
Array.append
vytvoří novou matici zkombinováním dvou existujících polí.
Následující kód ukazuje Array.append.
printfn "%A" (Array.append [| 1; 2; 3|] [| 4; 5; 6|])
Výstup předchozího kódu je následující.
[|1; 2; 3; 4; 5; 6|]
Array.choose
vybere prvky pole, které chcete zahrnout do nového pole. Následující kód ukazuje Array.choose
. Všimněte si, že typ prvku pole nemusí odpovídat typu hodnoty vrácené v typu možnosti. V tomto příkladu je int
typ prvku a možnost je výsledkem polynomické funkce, elem*elem - 1
jako číslo s plovoucí desetinou čárkou.
printfn "%A" (Array.choose (fun elem -> if elem % 2 = 0 then
Some(float (elem*elem - 1))
else
None) [| 1 .. 10 |])
Výstup předchozího kódu je následující.
[|3.0; 15.0; 35.0; 63.0; 99.0|]
Array.collect
spustí zadanou funkci na každém prvku pole existujícího pole a pak shromáždí prvky generované funkcí a zkombinuje je do nového pole. Následující kód ukazuje Array.collect
.
printfn "%A" (Array.collect (fun elem -> [| 0 .. elem |]) [| 1; 5; 10|])
Výstup předchozího kódu je následující.
[|0; 1; 0; 1; 2; 3; 4; 5; 0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10|]
Array.concat
přebírá posloupnost polí a kombinuje je do jednoho pole. Následující kód ukazuje Array.concat
.
Array.concat [ [|0..3|] ; [|4|] ]
//output [|0; 1; 2; 3; 4|]
Array.concat [| [|0..3|] ; [|4|] |]
//output [|0; 1; 2; 3; 4|]
Array.filter
vezme logickou funkci podmínky a vygeneruje nové pole, které obsahuje pouze ty prvky ze vstupního pole, pro které je podmínka pravdivá. Následující kód ukazuje Array.filter
.
printfn "%A" (Array.filter (fun elem -> elem % 2 = 0) [| 1 .. 10|])
Výstup předchozího kódu je následující.
[|2; 4; 6; 8; 10|]
Array.rev
vygeneruje nové pole vrácením pořadí existujícího pole. Následující kód ukazuje Array.rev
.
let stringReverse (s: string) =
System.String(Array.rev (s.ToCharArray()))
printfn "%A" (stringReverse("!dlrow olleH"))
Výstup předchozího kódu je následující.
"Hello world!"
Funkce v modulu pole, které transformují pole, můžete snadno kombinovat pomocí operátoru kanálu (|>
), jak je znázorněno v následujícím příkladu.
[| 1 .. 10 |]
|> Array.filter (fun elem -> elem % 2 = 0)
|> Array.choose (fun elem -> if (elem <> 8) then Some(elem*elem) else None)
|> Array.rev
|> printfn "%A"
Výstup je
[|100; 36; 16; 4|]
Multidimenzionální pole
Multidimenzionální pole lze vytvořit, ale neexistuje žádná syntaxe pro zápis literálu multidimenzionálního pole. Pomocí operátoru array2D
vytvořte pole z posloupnosti sekvencí prvků pole. Sekvence můžou být maticové nebo listové literály. Například následující kód vytvoří dvojrozměrné pole.
let my2DArray = array2D [ [ 1; 0]; [0; 1] ]
Pomocí funkce Array2D.init
můžete také inicializovat pole dvou dimenzí a podobné funkce jsou k dispozici pro pole se třemi a čtyřmi dimenzemi. Tyto funkce používají funkci, která se používá k vytvoření prvků. Chcete-li vytvořit dvojrozměrné pole, které obsahuje prvky nastavené na počáteční hodnotu místo určení funkce, použijte Array2D.create
funkci, která je také k dispozici pro pole až čtyři dimenze. Následující příklad kódu nejprve ukazuje, jak vytvořit pole polí, které obsahují požadované prvky, a pak používá Array2D.init
k vygenerování požadované dvojrozměrné pole.
let arrayOfArrays = [| [| 1.0; 0.0 |]; [|0.0; 1.0 |] |]
let twoDimensionalArray = Array2D.init 2 2 (fun i j -> arrayOfArrays[i][j])
Syntaxe indexování a řezů polí je podporovaná pro pole až do pořadí 4. Když zadáte index ve více dimenzích, použijete čárky k oddělení indexů, jak je znázorněno v následujícím příkladu kódu.
twoDimensionalArray[0, 1] <- 1.0
Typ dvojrozměrného pole je napsán jako <type>[,]
(například int[,]
, ), double[,]
a typ trojrozměrného pole je napsán jako <type>[,,]
a tak dále pro pole vyšších dimenzí.
Pro multidimenzionální pole je k dispozici pouze podmnožina funkcí dostupných pro jednorozměrná pole.
Vytváření řezů polí a multidimenzionální pole
V dvojrozměrném poli (matici) můžete extrahovat dílčí matici zadáním rozsahů a použitím zástupného znaku (*
) k určení celých řádků nebo sloupců.
// Get rows 1 to N from an NxM matrix (returns a matrix):
matrix[1.., *]
// Get rows 1 to 3 from a matrix (returns a matrix):
matrix[1..3, *]
// Get columns 1 to 3 from a matrix (returns a matrix):
matrix[*, 1..3]
// Get a 3x3 submatrix:
matrix[1..3, 1..3]
Multidimenzionální pole můžete rozložit do dílčích polí stejné nebo nižší dimenze. Vektor můžete například získat z matice zadáním jednoho řádku nebo sloupce.
// Get row 3 from a matrix as a vector:
matrix[3, *]
// Get column 3 from a matrix as a vector:
matrix[*, 3]
Tuto syntaxi řezů můžete použít pro typy, které implementují operátory přístupu elementu a přetížené GetSlice
metody. Následující kód například vytvoří typ Matice, který zabalí pole F# 2D, implementuje vlastnost Item, která poskytuje podporu indexování pole a implementuje tři verze GetSlice
. Pokud tento kód můžete použít jako šablonu pro typy matic, můžete použít všechny operace řezů, které tato část popisuje.
type Matrix<'T>(N: int, M: int) =
let internalArray = Array2D.zeroCreate<'T> N M
member this.Item
with get(a: int, b: int) = internalArray[a, b]
and set(a: int, b: int) (value:'T) = internalArray[a, b] <- value
member this.GetSlice(rowStart: int option, rowFinish : int option, colStart: int option, colFinish : int option) =
let rowStart =
match rowStart with
| Some(v) -> v
| None -> 0
let rowFinish =
match rowFinish with
| Some(v) -> v
| None -> internalArray.GetLength(0) - 1
let colStart =
match colStart with
| Some(v) -> v
| None -> 0
let colFinish =
match colFinish with
| Some(v) -> v
| None -> internalArray.GetLength(1) - 1
internalArray[rowStart..rowFinish, colStart..colFinish]
member this.GetSlice(row: int, colStart: int option, colFinish: int option) =
let colStart =
match colStart with
| Some(v) -> v
| None -> 0
let colFinish =
match colFinish with
| Some(v) -> v
| None -> internalArray.GetLength(1) - 1
internalArray[row, colStart..colFinish]
member this.GetSlice(rowStart: int option, rowFinish: int option, col: int) =
let rowStart =
match rowStart with
| Some(v) -> v
| None -> 0
let rowFinish =
match rowFinish with
| Some(v) -> v
| None -> internalArray.GetLength(0) - 1
internalArray[rowStart..rowFinish, col]
module test =
let generateTestMatrix x y =
let matrix = new Matrix<float>(3, 3)
for i in 0..2 do
for j in 0..2 do
matrix[i, j] <- float(i) * x - float(j) * y
matrix
let test1 = generateTestMatrix 2.3 1.1
let submatrix = test1[0..1, 0..1]
printfn $"{submatrix}"
let firstRow = test1[0,*]
let secondRow = test1[1,*]
let firstCol = test1[*,0]
printfn $"{firstCol}"
Logické funkce v polích
Funkce Array.exists
a Array.exists2
testovací prvky v jednom nebo dvou polích. Tyto funkce přebírají testovací funkci a vrátí true
se, pokud existuje prvek (nebo dvojice elementů), Array.exists2
který splňuje podmínku.
Následující kód ukazuje použití Array.exists
a Array.exists2
. V těchto příkladech jsou nové funkce vytvořeny použitím pouze jednoho z argumentů, v těchto případech argument funkce.
let allNegative = Array.exists (fun elem -> abs (elem) = elem) >> not
printfn "%A" (allNegative [| -1; -2; -3 |])
printfn "%A" (allNegative [| -10; -1; 5 |])
printfn "%A" (allNegative [| 0 |])
let haveEqualElement = Array.exists2 (fun elem1 elem2 -> elem1 = elem2)
printfn "%A" (haveEqualElement [| 1; 2; 3 |] [| 3; 2; 1|])
Výstup předchozího kódu je následující.
true
false
false
true
Podobně funkce Array.forall
testuje pole, aby určila, zda každý prvek splňuje logickou podmínku. Varianta Array.forall2
dělá totéž pomocí logické funkce, která zahrnuje prvky dvou polí se stejnou délkou. Použití těchto funkcí znázorňuje následující kód.
let allPositive = Array.forall (fun elem -> elem > 0)
printfn "%A" (allPositive [| 0; 1; 2; 3 |])
printfn "%A" (allPositive [| 1; 2; 3 |])
let allEqual = Array.forall2 (fun elem1 elem2 -> elem1 = elem2)
printfn "%A" (allEqual [| 1; 2 |] [| 1; 2 |])
printfn "%A" (allEqual [| 1; 2 |] [| 2; 1 |])
Výstup pro tyto příklady je následující.
false
true
true
false
Vyhledávací pole
Array.find
vezme logickou funkci a vrátí první prvek, pro který funkce vrátí true
, nebo vyvolá System.Collections.Generic.KeyNotFoundException , pokud není nalezen žádný prvek, který splňuje podmínku. Array.findIndex
je jako Array.find
, s tím rozdílem, že vrátí index prvku místo samotného prvku.
Následující kód používá Array.find
a Array.findIndex
k vyhledání čísla, které je perfektní čtverec i dokonalá datová krychle.
let arrayA = [| 2 .. 100 |]
let delta = 1.0e-10
let isPerfectSquare (x:int) =
let y = sqrt (float x)
abs(y - round y) < delta
let isPerfectCube (x:int) =
let y = System.Math.Pow(float x, 1.0/3.0)
abs(y - round y) < delta
let element = Array.find (fun elem -> isPerfectSquare elem && isPerfectCube elem) arrayA
let index = Array.findIndex (fun elem -> isPerfectSquare elem && isPerfectCube elem) arrayA
printfn "The first element that is both a square and a cube is %d and its index is %d." element index
Výstup je následující.
The first element that is both a square and a cube is 64 and its index is 62.
Array.tryFind
je jako Array.find
, s tím rozdílem, že jeho výsledek je typ možnosti a vrátí None
, pokud nebyl nalezen žádný prvek. Array.tryFind
místo Array.find
toho, když nevíte, zda je odpovídající prvek v poli. Podobně se podobá Array.findIndex
tomu, Array.tryFindIndex
že typ možnosti je návratová hodnota. Pokud nebyl nalezen žádný prvek, je tato možnost None
.
Následující kód ukazuje použití Array.tryFind
. Tento kód závisí na předchozím kódu.
let delta = 1.0e-10
let isPerfectSquare (x:int) =
let y = sqrt (float x)
abs(y - round y) < delta
let isPerfectCube (x:int) =
let y = System.Math.Pow(float x, 1.0/3.0)
abs(y - round y) < delta
let lookForCubeAndSquare array1 =
let result = Array.tryFind (fun elem -> isPerfectSquare elem && isPerfectCube elem) array1
match result with
| Some x -> printfn "Found an element: %d" x
| None -> printfn "Failed to find a matching element."
lookForCubeAndSquare [| 1 .. 10 |]
lookForCubeAndSquare [| 100 .. 1000 |]
lookForCubeAndSquare [| 2 .. 50 |]
Výstup je následující.
Found an element: 1
Found an element: 729
Failed to find a matching element.
Použijte Array.tryPick
, když potřebujete kromě jeho hledání transformovat prvek. Výsledkem je první prvek, pro který funkce vrátí transformovaný prvek jako hodnotu možnosti, nebo None
pokud se takový prvek nenajde.
Následující kód ukazuje použití Array.tryPick
. V tomto případě je místo výrazu lambda definováno několik místních pomocných funkcí pro zjednodušení kódu.
let findPerfectSquareAndCube array1 =
let delta = 1.0e-10
let isPerfectSquare (x:int) =
let y = sqrt (float x)
abs(y - round y) < delta
let isPerfectCube (x:int) =
let y = System.Math.Pow(float x, 1.0/3.0)
abs(y - round y) < delta
// intFunction : (float -> float) -> int -> int
// Allows the use of a floating point function with integers.
let intFunction function1 number = int (round (function1 (float number)))
let cubeRoot x = System.Math.Pow(x, 1.0/3.0)
// testElement: int -> (int * int * int) option
// Test an element to see whether it is a perfect square and a perfect
// cube, and, if so, return the element, square root, and cube root
// as an option value. Otherwise, return None.
let testElement elem =
if isPerfectSquare elem && isPerfectCube elem then
Some(elem, intFunction sqrt elem, intFunction cubeRoot elem)
else None
match Array.tryPick testElement array1 with
| Some (n, sqrt, cuberoot) -> printfn "Found an element %d with square root %d and cube root %d." n sqrt cuberoot
| None -> printfn "Did not find an element that is both a perfect square and a perfect cube."
findPerfectSquareAndCube [| 1 .. 10 |]
findPerfectSquareAndCube [| 2 .. 100 |]
findPerfectSquareAndCube [| 100 .. 1000 |]
findPerfectSquareAndCube [| 1000 .. 10000 |]
findPerfectSquareAndCube [| 2 .. 50 |]
Výstup je následující.
Found an element 1 with square root 1 and cube root 1.
Found an element 64 with square root 8 and cube root 4.
Found an element 729 with square root 27 and cube root 9.
Found an element 4096 with square root 64 and cube root 16.
Did not find an element that is both a perfect square and a perfect cube.
Provádění výpočtů v polích
Funkce Array.average
vrátí průměr každého prvku v matici. Je omezena na typy prvků, které podporují přesné dělení celočíselným číslem, které zahrnuje typy s plovoucí desetinou čárkou, ale ne celočíselné typy. Funkce Array.averageBy
vrátí průměr výsledků volání funkce na každém prvku. Pro matici celočíselného typu můžete použít Array.averageBy
a nechat funkci převést každý prvek na typ s plovoucí desetinou čárkou pro výpočet.
Pokud typ elementu podporuje, použijte Array.max
nebo Array.min
získáte maximální nebo minimální prvek. Podobně a Array.minBy
povolit, Array.maxBy
aby se funkce nejprve spustila, možná transformovat na typ, který podporuje porovnání.
Array.sum
přidá prvky pole a Array.sumBy
zavolá funkci pro každý prvek a výsledky sečte dohromady.
Chcete-li spustit funkci pro každý prvek v poli bez uložení vrácených hodnot, použijte Array.iter
. Pro funkci zahrnující dvě matice se stejnou délkou použijte Array.iter2
. Pokud potřebujete také zachovat pole výsledků funkce, použít Array.map
nebo Array.map2
, která pracuje se dvěma poli najednou.
Varianty Array.iteri
a Array.iteri2
umožňují index prvku být zapojen do výpočtu; totéž platí pro Array.mapi
a Array.mapi2
.
Funkce Array.fold
, , Array.foldBack
Array.reduce
, Array.reduceBack
, Array.scan
a Array.scanBack
spouštění algoritmů, které zahrnují všechny prvky pole. Podobně varianty Array.fold2
a Array.foldBack2
výpočty provádějí na dvou polích.
Tyto funkce pro provádění výpočtů odpovídají funkcím stejného názvu v modulu List. Příklady použití najdete v tématu Seznamy.
Úprava polí
Array.set
nastaví prvek na zadanou hodnotu. Array.fill
nastaví rozsah prvků v matici na zadanou hodnotu. Následující kód poskytuje příklad .Array.fill
let arrayFill1 = [| 1 .. 25 |]
Array.fill arrayFill1 2 20 0
printfn "%A" arrayFill1
Výstup je následující.
[|1; 2; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 23; 24; 25|]
Můžete použít Array.blit
ke zkopírování pododdílu jednoho pole do jiného pole.
Převod na jiné typy a z jiných typů
Array.ofList
vytvoří pole ze seznamu. Array.ofSeq
vytvoří pole z sekvence. Array.toList
a Array.toSeq
převeďte na tyto další typy kolekcí z typu pole.
Řazení polí
Slouží Array.sort
k seřazení pole pomocí obecné porovnávací funkce. Slouží Array.sortBy
k určení funkce, která vygeneruje hodnotu, která se označuje jako klíč, k řazení pomocí obecné porovnávací funkce na klíči. Použijte Array.sortWith
, pokud chcete zadat vlastní funkci porovnání. Array.sort
, Array.sortBy
a Array.sortWith
všechny vrátí seřazené pole jako novou matici. Varianty Array.sortInPlace
a Array.sortInPlaceBy
Array.sortInPlaceWith
upravte existující pole namísto vrácení nového pole.
Pole a řazené kolekce členů
Funkce Array.zip
a Array.unzip
převod polí párů řazené kolekce členů na řazené kolekce členů a naopak. Array.zip3
a Array.unzip3
jsou podobné s tím rozdílem, že pracují s řazenými kolekcemi členů tří prvků nebo řazených kolekcí členů tří polí.
Paralelní výpočty v polích
Modul Array.Parallel
obsahuje funkce pro provádění paralelních výpočtů v polích. Tento modul není k dispozici v aplikacích, které cílí na verze .NET Framework před verzí 4.